본문 바로가기

FrontEnd

타입스크립트의 타입 불건전성에 대하여 2 : 공식 예제 코드 살펴보기

반응형

https://www.typescriptlang.org/play?strictFunctionTypes=false#example/soundness 

 

TS Playground - An online editor for exploring TypeScript and JavaScript

The Playground lets you write TypeScript or JavaScript online in a safe and sharable way.

www.typescriptlang.org

 

타입 이론에 대한 배경 지식이 없으면, "건전성"이라는 타입 시스템의 개념에 익숙하지 않을 것입니다. 
건전성은 컴파일러가 런타임 값이 컴파일 타임과 런타임에 같은 타입임을 보장할 수 있다는 것을 의미합니다. 
 
처음부터 정적 언어로 설계된 언어에서 이는 당연합니다. 언어의 설계는 단순성, 사용성, 건전성의 절충입니다. 
비록 건전성을 보장하는 타입 이론이 수십년 전부터 존재해왔음에도, 
타입스크립트는 모든 자바스크립트의 호환과 사용성, 단순성을 위해 건전성을 절충했습니다. 
아래 코드에서 타입스크립트가 건전하지 않은 예시들을 살펴봅시다.

1. Type assertions

const usersAge = ("23" as any) as number;

타입스크립트는 타입 단언을 통한 추론 오버라이딩을 허용합니다.

이는 당신이 타입스크립트보다 타입을 더 잘 알고 있으며, 해당 타입을 적용하는 것에 당신이 책임지는 것에 동의하는 것을 의미합니다.

건전한 언어들을 종종 런타임에도 타입을 검사합니다.

하지만 타입스크립트는 런타임에 타입 체크를 하지않는 것을 목표로 합니다.

 

타입 건전성 확보하기

1. 타입 단언보다 타입 선언 사용하기.(:Type)

2. 생성자 함수 등을 이용하여 런타임 타입 체크 밋 타입 생성 활용

3. 모나드 활용(Optional)

4. 꼭 써야 하는 경우에만 활용하기 (API리턴값, JSON.parse와 같은 any, DOM 접근)

(주 : 코드 레벨에서 해당 타입이라는 것을 체크해주기만 하면 타입 건선성은 유지된다.)

 

 

2.  Function Parameter Bi-variance(이분산)

함수의 매개변수가 매개변수 재정의를 지원하는 경우입니다.

interface InputEvent {
  timestamp: number;
}
interface MouseInputEvent extends InputEvent {
  x: number;
  y: number;
}
interface KeyboardInputEvent extends InputEvent {
  keyCode: number;
}

function listenForEvent(eventType: "keyboard" | "mouse", handler: (event: InputEvent) => void) { }

매개변수 타입을 하위 타입으로 다시 선언할 수 있습니다.

위에서 핸들러는 InputEvent 유형을 예상했습니다.

그러나 아래 사용 예에서 - TypeScript는 추가 속성이 있는 타입을 허용합니다.

listenForEvent("keyboard", (event: KeyboardInputEvent) => { });
listenForEvent("mouse", (event: MouseInputEvent) => { });
하지만 실제 함수 파라미터는 이분산(Bivariance)입니다. 즉, 이분산은 상위 유형 및 하위 유형을 모두 허용합니다!
// 이것은 최소 공통 타입을 허용합니다. (다운캐스팅)
listenForEvent("mouse", (event: {}) => { });

// But no further: 하위 유형이 아니기 때문입니다.
listenForEvent("mouse", (event: string) => { });

해당 오류는 strictFunctionTypes을 통해 해결할 수 있습니다.

그러면 함수 파라미터가 반공변(contravariance-상위타입 허용)을 허용합니다.

재밌게도 리턴 타입은 공변(covariant-하위타입 허용)입니다.

type F<T> = (p: T) => T
인자(Parameter)는 반공변성 흐름을 가지고,
반환(Return)은 공변성 흐름을 가지기 때문에 F<A> 와 F<B> 간에 어떤 형태의 Type Cast 도 일어날 수 없다.
T => T 는 무공변성(Invariance) 일 수 밖에 없다.
Type Cast 까지 고려를 한다면 반공변성 문제로 인해서 type F<P, R> = (p: P) => R 이 되어야만 한다.
  type A = {a: string};
  type B = {a: string, b: string};
  type C = {a: string, b: string, c: string};
  
  type F<P, R> = (p: P) => R;
  
  const a: A = {a: 'a'};
  const b: B = {a: 'a', b: 'b'}; // A는 B의 슈퍼타입
  const c: C = {a: 'a', b: 'b', c: 'c'}; // B는 C의 슈퍼타입.
  
  const faa: F<A, A> = (p: A) => a;
  const fab: F<A, B> = (p: A) => b;
  const fac: F<A, C> = (p: A) => c;
  const fba: F<B, A> = (p: B) => a;
  const fbb: F<B, B> = (p: B) => b;
  const fbc: F<B, C> = (p: B) => c;
  const fca: F<C, A> = (p: C) => a;
  const fcb: F<C, B> = (p: C) => b;
  const fcc: F<C, C> = (p: C) => c;
  
  const fa1: F<C, A> = faa; // 인자의 반공변 (슈퍼타입 허용 - C의 슈퍼타입인 AB 다허용)
  const fa2: F<C, A> = fab; // 리턴타입의 공변 (서브타입 허용 - A의 서브타입인 BC 다허용)
  const fa3: F<C, A> = fac; 
  const fb1: F<C, A> = fba;
  const fb2: F<C, A> = fbb;
  const fb3: F<C, A> = fbc;
  const fc1: F<C, A> = fca;
  const fc2: F<C, A> = fcb;
  const fc3: F<C, A> = fcc;
  
  const list: F<C, A>[] = [
      faa,
      fab,
      fac,
      fba,
      fbb,
      fbc,
      fca,
      fcb,
      fcc,
    ];

나머지 매개변수(Rest Parameter)

나머지 매개변수는 모두 옵셔널한 값으로 간주됩니다.
즉, TypeScript는 매개변수의 총 갯수를 알 방법이 없습니다.
(콜백에 사용할 수 있는 매개변수.)
function getRandomNumbers(count: number, callback: (...args: number[]) => void) { }

getRandomNumbers(2, (first, second) => console.log([first, second]));
getRandomNumbers(400, (first) => console.log(first));

보이드 리턴 함수 타입은 다른 타입을 리턴하는 함수를 허용합니다.

주 : 이는 주로 콜백 형태로 함수를 넘겨 combinator 역할을 하는 함수의 리턴값을 사용하지 않는 경향이 있는 js의 특성을 반영한 허용임
const getPI = () => 3.14;

function runFunction(func: () => void) {
  func();
}

runFunction(getPI);

참고 : 

https://www.typescriptlang.org/docs/handbook/type-compatibility.html

 

Documentation - Type Compatibility

How type-checking works in TypeScript

www.typescriptlang.org

https://iamssen.medium.com/typescript-%EC%97%90%EC%84%9C%EC%9D%98-%EA%B3%B5%EB%B3%80%EC%84%B1%EA%B3%BC-%EB%B0%98%EA%B3%B5%EB%B3%80%EC%84%B1-strictfunctiontypes-a82400e67f2

 

TypeScript 에서의 공변성과 반공변성 (strictFunctionTypes)

strictFunctionTypes 에서 등장하는 공변성 (Convariance), 반공변성 (Contravariance) 말이 어려워서 혼돈이 있을수 있는데, 한 마디로 정리하자면 일반적인 Sub Type 관계가 함수 인자에서는 반대로 적용된다는

iamssen.medium.com

https://dev.to/codeoz/how-i-understand-covariance-contravariance-in-typescript-2766

 

How I understand Covariance & Contravariance in typescript

Covariance, contravariance, bivariance... Theses words seems unfamiliar, difficult to understand for...

dev.to

 

반응형