Typescript ( CFA )
2025. 5. 15. 11:35
728x90
반응형

CFA ( Control Flow Analysis )

  • 제어 흐름 분석(Control Flow Analysis) 은 변수의 값, 타입, 상태 등을 코드의 실행 흐름에 따라 자동으로 추론하고 추적하는 TypeScript의 분석 메커니즘입니다.
  • 이 분석은 특히 유니언 타입을 가진 값에서 불필요한 타입을 제거하여 보다 정확한 타입으로 좁히는 데 집중합니다. 이를 narrowing 과정이라고 합니다. 대부분의 경우 자바스크립트의 자연스러운 불리언 로직을 기반으로 작동하며, 조건문, 논리 연산, 단축 평가 등의 흐름을 통해 타입 정보를 추적합니다. 변수에 대한 타입 정보는 제어 흐름 상에서 조건에 따라 변경되며, 그 시점에서 가능한 타입만을 고려하여 타입을 재구성합니다.
  • 대부분의 narrowing 과정은 조건문(if, else, switch 등) 내부에서 발생하며, typeof, instanceof, null/undefined 검사, 속성 존재 검사 등을 통해 타입이 좁혀진다. 또한, 코드 라인 내의 불리언 표현식에서도 narrowing 과정이 이루어지며, 단축 평가나 조건 연결에서도 정확한 타입 추론이 가능하다.
  • 이러한 내장 로직 외에도 사용자 정의 타입 가드 함수 ( val is T ) 를 통해 narrowing 동작을 확장할 수 있게 한다. 사용자가 정의한 타입 판별 함수를 통해 CFA는 함수 호출 결과를 기반으로 타입을 좁힌다. CFA는 이러한 흐름 분석을 바탕으로 실행 시점에서 접근 가능한 값의 타입만을 허용함으로써, 타입 안전성과 코드 안정성을 향상시킨다.

 

If Statements ( if 문 )

  • 대부분의 narrowing은 if, else, &&, ||, ! 같은 논리 연산자를 기반으로 발생합니다.
// typeof (for primitives)

const input = getUserInput()
input // string | number

if(typeof input === 'string'){ // typeof 연산자는 런타임에 원시 타입을 문자열로 판별.
	input // string
}

// interfaceof for classes)

const input = getUserInput()
input // number | number[]

if(input instansceof Array) { // instanceof는 객체가 특정 클래스의 인스턴스인지 검사.
	input // number[]
}

// "property" in object (for objects)

const input = getUserInput()
input // string | { error : ... }

if("error" in input) { // "property" in 연산자는 객체에 해당 속성이 존재하는지 검사.
	input // { error : ... }
}

// type-guard functions (for anything)

const input = getUserInput()
input // number | number[]

if (Array.isArray(input)) { // Array.isArray는 내장 타입 가드 함수이며, input is any[] 형태의 narrowing 정보를 포함.
	input // number[]
}

// expressions

const input = getUserInput()
input // string | number

const inputLength = ( typeof input === 'string' && input.length ) || input 
// input : string
// 이처럼 표현식 내에서도 CFA를 적용하여 narrowing을 처리함.

 

Discriminated Unions ( 식별된 유니언 )

  • 여러 객체 타입을 공통된 리터럴 속성(discriminant)을 기준으로 구분하여, Control Flow Analysis(CFA)가 각 타입을 정확히 좁힐 수 있게 해주는 유니언 타입입니다.
type Responses = { 
	status: 200 , data: any
} | {
	status: 301 , to: string
} | {
	status: 400 , error: Error
}
// status : 공통의 discriminant 속성. 
// status는 리터럴 타입(200, 301, 400)으로 설정되어 있으며, 이 덕분에 TypeScript는 해당 값을 기준으로 타입을 자동 추론(narrowing)할 수 있습니다.

const response = getResponse()
response // Responses

switch(response.status){
	case 200 : return response.data
    case 301 : return redirect(response.to)
    case 400 : return response.error
}

 

Type Guards ( 타입 가드 )

  • 런타임 조건을 통해 변수의 타입을 좁히는 구문 또는 함수입니다. Type Guard를 통해 코드의 흐름에 따라 유니언 타입에서 특정 타입을 분리해서 처리할 수 있도록 도와줍니다.
function isErrorResponse(obj: Response) : obj is APIErrorResponse {
// obj is APIErrorResponse는 타입 좁히기 정보를 TypeScript에 명시적으로 제공함
	return obj instanceof APIErrorResponse
}

const response = getResponse()
response // Response | APIErrorResponse

if (isErrorResponse(response)) {
	response // APIErrorResponse
}

 

Assertion functions ( 단언 함수 )

  • TypeScript에서 런타임 검사를 수행하면서 컴파일러에게 변수의 타입을 알려주는 함수입니다. 이는 Type Guards와 비슷하지만, true/false를 반환하지 않고 오류를 throw하며, 타입을 "단언"합니다.
function process(input: unknown) {
  assertIsString(input);  // 런타임 검사 + 타입 단언
  // 여기서부터 input은 string으로 추론됨
  console.log(input.toUpperCase());
}

function assertIsString(value: unknown): asserts value is string { // 이 함수가 반환되면 value는 무조건 string임이 보장됨
  if (typeof value !== 'string') {
    throw new Error('Expected a string');
  }
}
// input의 타입은 원래 unknown
// assertIsString이 호출된 후, TypeScript는 input을 string으로 간주함

 

Assignment ( 타입 할당 )

  • assignment(할당)는 타입 추론과 타입 좁히기(narrowing) 흐름에 직접적인 영향을 주는 중요한 요소입니다. 
  • TypeScript의 CFA는 조건문 등을 통해 좁혀진 타입 정보를 유지합니다. 그러나 변수에 새로운 값이 할당되면 이전에 narrowing된 정보는 무효화됩니다.
let val: string | null = getInput();

if (val !== null) {
  // val은 string으로 narrowing됨
  val = val.trim(); // 여전히 string

  val = null;
  // 이후 val은 string | null로 다시 widen됨
}

function getValue(): unknown {
  return "something";
}

const value = getValue() as string;
// 이제 value는 string으로 간주되므로 string 메서드 사용 가능
console.log(value.toUpperCase());
728x90
반응형

'typescript' 카테고리의 다른 글

Typescript 5  (1) 2025.05.19
Typescript ( Structural Typing )  (0) 2025.05.13
Typescript ( type vs interface )  (0) 2025.05.11