2 주차 학습 ✨

작성일: 2023년 9월 16일 오후 11:24

2.10 객체의 속성과 메서드에 적용되는 특징

  • 객체의 속성에도 옵셔널이나 readonly 수식어 가능

      interface IObj  {
          normalInfo? : string;   
          readonly privateInfo : string; 
      }
    
    • readonly 수식어가 붙은 요소는 변경할 수 없다.
    • ? 수식어가 붙은 속성은 undefined | 할당한 타입 으로 추론됨
      • 그래서 ? 수식어가 붙은 속성은 undefined를 직접 할당가능
    • 여러 수식어를 한 번에 사용할 수 있다.
      • 왜 readonly 수식어는 앞에 붙이지란 의문이 있었는데, 여러 수식어를 붙일 때 식별할 수 있게 도와주네.
  • 객체 리터럴과 객체 리터럴을 참조하는 변수에 따른 타입 검사 방식

    • 객체 리터럴

        interface IType = {
            name : string;
        }
      
        const isNotOk : IType = { 
            name : 'hi', say:'hellow' // error say 없어
        }
      
    • 변수에 할당 후

        const 변수 = { name : 'mahwin', say:'hellow' }
        const isOk : IType = 변수;
      
    • 매개변수에서도 정확히 같은 일이 일어난다.

        interface ICoin {
            coin :string,
            price: number,
        }
      
        function getCoinInfoArr(coinInfo1:ICoin, coinInfo2:ICoin) :ICoin[] {
            return [coinInfo1,coinInfo2]
        }
        const coinInfo = {coin:"에이다", price:"33"}
      
        getCoinInfoArr(coinInfo, {coin:"에이다", price:"33"})
        // 두 번째 매개변수에서 에러 나옴!
        // 정확히 같은 객체를 (참조값을 의미하는 것은 아님, 타입적으로)
        // 넣어도 다른 결과가 나오는 이유는 잉여 속성 검사 때문
      
    • 잉여 속성 검사

      • 단순하게 타입을 검증할 때 선언하지 않은 속성이 있는지 체크해줌
      • only 객체 리터럴에서만 체크 해줌
      • 참조값으로 타입을 체크할 때는 객체 간 대입 가능성을 기준으로 함
  • 객체에서도 전개 문법과 나머지 속성을 이용할 수 있다.

      const {prop:{nested,...rest}} =
      {prop:{nested:'hi', name:'crong',age:4}};
      // nested의 타입 nested:string
      // rest의 타입 name:string, age: number
    

💡 타입스크립트의 error 메세지는 아래로 내려갈 수록 구체적인 정보를 포함하고 있다.

2.10.1 인덱스 접근 타입

  • 자바스크립트 객체 처럼 타입의 속성도 접근할 수 있다.

      type Animal = {
          name: string;
      }
      type N1 = Animal['name' | "name"] // ok
      type N2 =Animal.name; // X
    
  • 인덱스 접근 타입을 활용한 객체의 키 타입 얻기

      const myName ={
          first:"정",
          second:"유",
          third:"석"
      }
      type keys = keyof typeof myName;
      // typeof 연산자는 객체의 타입을 가져오고,
      // keyof 연산자는 해당 객체의 속성 키들을 
      // 문자열 리터럴 유니온 타입으로 
      // "first"|"second"|"third"
    
  • 인덱스 접근 타입을 활용한 객체의 타입 얻기

      // 앞에서 얻은 keys
      type values = typeof myName[keys];
      // 해당 값들의 타입을 유니온 타입으로 
      // string
    
  • keyof 특성

      type anyTypeKeys =  keyof any; //  string|number|symbol
    
    • string과 symbol만 key 값으로 가능하지만 타입스크립트는 배열을 위해 number 키도 허용한다.

      type arrKeysType = keyof [1,2,3];
      
    • keyof 배열

      • number | 배열속성이름유니언 | 배열인덱스문자열유니언
      • 배열속성이름_유니언
        • 배열이 공통적으로 갖고 있는 속성 ( length, forEach, indexOf ….)
      • 배열인덱스문자열_유니언
        • 배열의 인덱스를 유니온한 값 ‘0’|’1’|’2’
    • arrKeysType에 ‘3’은 안 되지만 3은 포함된다

      • number
      • 배열속성이름_유니언
        • forEach | map | filter | reduce | indexOf | …
      • 배열인덱스문자열_유니언
        • ‘0’ | ‘1’ | ‘2’
      type numArrType =  [1,3,5];
      type First = numArrType[0]; // 1
      type Length =numArrType['length']; // 3
      
      type booleanOrStringArrType = (string|boolean)[];
      type elementType =  booleanOrStringArrType[number]; 
      // elementType는 string | boolean으로 추론됨!
      
    • 인덱스 접근 타입을 활용해서 특정 키들의 값만 가져올 수도 있음

      const obj ={
        key1 : "hi"
        key2 : "hello"
        key3 : boolean;
      }
      
      type KeyType = typeof obj['key1'|'key2'];
      // KeyType는 string으로 추론!
      

2.10.2 매핑된 객체 타입

  • 기존의 타입으로 부터 새로운 객체 속성을 만들어 내는 것을 의미한다.
    • 인터페이스(X) 타입 별칭 (O)
  • in 연산자를 이용해서 인덱스 시그니처가 표현하지 못하는 타입을 표현할 수 있다.

    • 일부 속성에만 타입을 부여해보자
    • hello ,hi 를 키로 가진 값들의 속성을 string으로 하고 싶으면 어떻게 타입을 지정해줘야 할까

      type errorType = {
        [key : "hello"|"hi"] : string; //Error
      }
      // 인덱스 시그니처에 사용할 수 있는 타입은 string, number, symbol,
      // 템플릿 리터럴과 이들의 유니온 뿐임
      // 템플릿 리터럴 타입의 예시 type Greeting = `Hello, ${string}!`;
      
    • 맵핑된 객체 타입을 이용해서 해결 가능하다.

      • 위 코드처럼 타입을 지정해도 돌아가는 것이 문제 없어 보이지만,
      • in 연산자를 붙힘으로 ts가 유니온 값을 통해 타입을 반복적으로 지정해 줘야 하구나라고 알 수 있음.
      type correctType = {
        [key in "hello"|"hi"] : string; // correct
      }
      // 유니온 타입의 값이 하나씩 평가되어 객체의 속성이 된다.
      
    • 맵핑된 객체 타입의 예시

      interface Original {
        name: string;
        age: number;
        married:boolean;
      }
      
      type Copy ={ 
        [key in keyof Original] : Original[key]
      }
      
      // Copy {name: string  age:number married:boolean}
      
    • 여기서 in 연산자가 확실히 필요하구나 알게 됨.

      • in 연산자가 포함되면 전체 구문이 평가되는 구나!
    • 튜플에서 매핑된 객체 타입 적용하기

      type Tuple = [1,2,3]
      type CopyTuple = {
        [key in keyof Tuple] = Tuple[key];
      }
      

      type CopyTuple = { [x:number] : 2|1|3;
      0: 1; 1: 2; 2: 3; length: 3; // 내부적으로 포함된 메소드들 pop:()=> 2 | 1 | 3 | undefined ... }

      // 왜 굳이 2|1|3 ?
      
    • 배열에서 매핑된 객체 타입 적용하기

      type Arr = number[];
      type CopyArr = {
        [key in keyof Arr] : Arr[key];
      }
      
      const copyArr: CopyArr = [1,3,9];
      

      type CopyArr = { [x:number] : number; length: number;
      // 내부적으로 포함된 메소드들 pop:()=> number|undefined; ...
      }

    • 맵핑된 객체 타입을 이용하면서 연산자 붙이기 가능!

      type Copy = {
        readonly [key in unionType]? : string;
      }
      

      type Copy = { readonly key1? : string | undefined; readonly key2? : string | undefined; readonly key3? : string | undefined; }

      • 연산자를 통해 수식어를 제거할 수도 있다.
      type mutableType = {
        -readonly [key in unionType]-? : string;
      }
      

      type mutableType = { key1 : string; key2 : string; key3 : string; }

2.11 타입을 집합으로 생각하자

  • 집합개념을 타입에 적용하자

    • & 교집합 | 합집합
    • 모든 타입을 포함하는 타입 unknown
    • 모든 타입에 포함되지 않는 타입 never

      type nev = string & number; // never로 추론
      
  • 타입스크립트에서는 좁은 타입을 넓은 타입에 대입해야 한다

    • unknown에 모든 타입은 대입할 수 있다.
      • 반대로 any와 unknown을 제외하고는 unknown을 대입할 순 없다.
    • never에는 모든 타입에 대입할 수 없다.
      • 반대로 모든 타입에 never는 대입할 수 있다.
    • any 타입은 집합 관계를 무시하기 지양하자.
  • & , | 연산자 사용 예시

      type A = string | boolean;
      type B = boolean | number ;
      type C =  A & B;   // boolean;
    
      type D = {} & (string | null ); // string
      // {} 는 null과 undefined를 제외한 모든 값
    
      type E = string & boolean; // never
    
      type F = unknown | {};  // unknown 
      type G = never & {}; // never
    
      // 주의할 점
    
      type I = null & {a:'b'}; // never
      type J = {} & string // string
    
      type H = {a:'b'} & number; // type H는  {a:'b'} & number
    

    type H는 never로 추론되는 게 맞아 보이지만 원시 자료형(null, undefined 제외)과 채워 진 객체를 &해도 never가 나오진 않는다.

     
    

2.12 타입도 상속이 가능하다.

  • extends를 이용한 타입 상속 with interface

      interface Human {
          name: string;
      }
      interface Mahwin extends Human {
          speak():void
      }
    
  • & 연산자를 이용한 타입 상속 with type

    • 상속 받는 다는 것은 더 좁은 타입이 만들어 진다는 것을 의미하기 때문에 &연산자를 사용

      type Human {
        name:string;
      }
      type Mahwin =  Human & {
        speak():void;
      }
      
  • type 별칭과 interface 섞어서 상속하기

      type Human {
          name:string;
      }
      interface Mahwin extends Human {
          speak():void;
      }
      interface YouSeock extends Human {
          coding():void;
      }
    
      // 한 번에 여러 타입 상속 받기
      interface Me extends Mahwin,YouSeock {}
    
      // 부모 속성의 타입을 변경할수도 있음
      interface Merge {
          one:string
      }
      interface Merge2 extends Merge{
          one:'a' // ok
      }
      // 대입 못하는 타입으로 변경하는 것은 불가능하다.
      // one:number  error
    

2.13 객체 간에 대입할 수 있는지 확인하는 법

  • 좁은 타입은 넓은 타입에 대입할 수 있지만 넓은 타입은 좁은 타입에 대입할 수 없다.

      interface IWide {
          name:string;
      }
    
      interface INarrow extends IWide {
          age:number;    
      }
    
      const wideObj = {name:"zero"};
      const narrowObj = {name:"zero" , age:32};
    
      // 알맞게 넣은 타입
      const wideToWide : IWide = wideObj // ok
      const narrowToNarrow : INarrow = narrowObj // ok
    
      // 잉여 속성 검사 무시 참조값으로 넣으니까
      const narrowToWide : IWide = narrowObj // ok
      const wideToNarrow : INarrow = wideObj // error 
    
      // 리터럴로 넣어줬다면 narrowToWide,wideToNarrow 둘다 에러남
      // narrowToWide age를 할당할 수 없다고 얘기하고
      // wideToNarrow는 더 넓은 타입을 좁은 타입에 할당할 수 없다고 나옴
      const narrowToWide : IWide = {name:"zero" , age:32}; // error
      const wideToNarrow : INarrow = {name:"zero"};        // error
    
  • 객체 타입에서 | 연산

      interface A {
          name :string;
      }
      interface B {
          age:number;
      }
      function fn() A|B { 
           // .. 
      }
    
      // 합집합은 각자의 집합이나 교집합보다 넓다
      // 넓은 타입에 좁은 타입을 대입할 순 없다.
    
      const target1:A&B = fn(); // error
      const target2:A = fn(); // error
      const target3:B = fn(); // error
    
  • 튜플과 배열

    • 튜플은 배열보다 좁은 타입이다.
    • 좁은 타입은 튜플을 배열에 대입할 순 있으나 넓은 타입인 배열을 튜플에 넣을 순 없다.

        let a:['hi','read'] = ['hi','read'];
        let b: string[] = ['hi','read'];
        a=b; // error 넓은 타입인 배열을 튜플 타입에 넣으려니 error 발생
        b=a;
      
    • 배열과 튜플에서 readonly 수식어

      • readonly 수식어가 붙은 타입이 더 넓다.

        let a : readonly string[] = ['a','b'];
        let b : string[] = ['a','b'];
        
        a=b; 
        b=a; // error readonly가 붙은 a가 더 넓은 타입이라 에러
        
    • readonly 튜플과 일반 배열의 타입 비교

        let a:readonly ['a','b'] = ['a','b'];
        let b:string[] = ['a','b'];
        a=b; // error 넓은 타입을 좁은 타입에 넣으면 안된다 (튜플,배열)
        b=a; // error readonly가 붙으면 일반 배열보다 넓은 의미라서
      
  • {} 의 타입 비교

    • 옵셔널 속성에 따른 타입
    • 더 넓은 타입은 좁은 타입에 대입할 수 없다가 되려면 optionalObj에 a,b 프로펄티 둘 다 채워야하지 않을까..?

      type optionalType {
        a?:string;
        b?:string;
      }
      type fixedType{ a:string; b:string }
      
      const optionalObj : optionalType = {a:"hi", b:"hello"}
      const fixedObj : fixedType = {a:"hi",b:"hellow"}
      
      const o2: optionalType = fixedObj;
      const m2: fixedType = optionalObj; // error
      // 옵셔널 때문에 a가 string | undefined로 더 넓은 객체가 됨.
      
    • readonly 속성에 따른 타입

      • readonly 속성은 {} 에서는 타입에 영향을 미치지 않는다.
      type optionalType {
        readonly a:string;
        readonly b:string;
      }
      type fixedType{ a:string; b:string }
      
      const optionalObj : optionalType = {a:"hi", b:"hello"}
      const fixedObj : fixedType = {a:"hi",b:"hellow"}
      
      const o2: optionalType = fixedObj;
      const m2: fixedType = optionalObj;
      

2.13.1 구조적 타이핑

  • 타입스크립트에서는 구조가 같다면 같은 타입으로 판단한다.
  • 정확히는 충분 조건을 따짐.
interface 주식 {name:string; price:number};
interface 코인 {name:string; price:number};
const 에이다 : 코인 = {amount:1,price:200};
const 셀트리온 : 주식 = 에이다;

interface A {a:string;}
interface B {a:string; b:string;}
// B 타입에 A 타입에 할당 o B는 A이기 위해 충분
// A 타입은 B 타입에 할당 x A는 B이기 위해 불충분
  • 매핑된 객체 타입에 적용된 구조적 타이핑
type numArrType = number[];
type CopyType = {
    [key in keyof numArrType] : numArrType[key];
}

const copy: CopyType = [1,3,9];
// CopyType는 객체 타입인데도 숫자 배열을 대입할 수 있음.
// 구조적 타이핑 때문.

type SimpleArr = {[key:number]:number, length:number};
const simpleArr :SimpleArr = [1,2,3];
  • 구조는 같지만 다른 타입으로 분리하고 싶다면?
    • __type과 같이 브랜드 속성을 붙여준다.
interface 주식 {__type:'주식'; name:string; price:number};
interface 코인 {__type:'코인'; name:string; price:number};

2.14 제네릭으로 타입을 함수처럼 사용하기

  • 제네릭은 <>으로 표기하며 실제 타입 인수를 매개변수 처럼 사용할 수 있다.
  • 타입 선언의 중복을 해결
  • 일반적으로 대문자로 표현

    ```tsx interface Array{

    [key:number]:T,
    length:number,
    // 기타 속성
}
// T가 string string 배열
// T가 boolean boolean 배열
```
  • 클래스, 타입 별칭에서 제네릭 사용하기

      type Person<N,A> = {
          name:N,
          age:A
      }
    
      class Person<N,A>{
          name:N;
          age:A;
          constructor (name:N, age:A){
              this.name = name;
              this.age = age; 
          }
      }
    
  • 함수에서 제네릭 사용하기

      //함수 표현식
      const fn = <N,A>(name:N,age:A) =>{};
      //함수 선언식
      function fn2<N,A>(name:N,age:N){};
    
  • interface와 type간 교차도 가능

      interface IPerseon<N,A>{
          name:N,
          age:A
      }
      type TPerson <N,A> = {
          name:N,
          age:A,
      }
    
      type Zero = IPerson<'zero', 29>;
      interface Nero extends TPerson('nero',32){}
    
  • 제네릭의 위치

    • interface 이름<>
    • type 이름<>
    • class 이름<>
    • function 이름<>
    • const 함수 = <>()
  • 특정 메서드에 제네릭 적용하기

      class Person<N,A>{
          ...
          method<B>(param:B){}
      }
      interface IPerson<N,A>{
          ...
          method: <B>(param:B) => void;
      }
    
  • 타입 매개변수에 default type 사용가능

      iinterface P<N=string, A= number) ...
    
    • 예시

      interface Person<N,A>{name:N,age:A};
      const getPerson = <N,A=unknown>(name:N,age:A) : Person=>({name,age})
      const zero = getPerson('z',222);
      // Person<string,number> 으로 추론
      // unknown보다 좁은 number로
      
  • 상수 타입 매개변수

      function values<T>(a:T[]){
          return {
              hasValue(value:T) {return a.includes(value)}
          }
      }
      const s = values(['a','b','c']);
      // T는 string으로 추론됨. 'a'| 'b'| 'c'와 같은 유니온으로 추론되게 하고 싶으면?
    
      // 4,9 버전 전에는
      function values<T>(a: readonly T[]){
          return {
              hasValue(value:T) {return a.includes(value)}
          }
      }
      const s = values(['a','b','c']);
    
      // 5.0 이상 버전에서는 
      function values<const T>(a:T[]){
          return {
              hasValue(value:T) {return a.includes(value)}
          }
      }
      const s = values(['a','b','c']);
    

2.14.1 제네릭에 제약 걸기

  • 타입 매개변수에 extends 문법을 사용해서 매개변수 타입에 제약을 걸 수 있다.

      interface E<A extends number, B= string>{
          a:A,
          b:B,
      }
    
  • 하나의 매개변수 타입이 다른 매개변수의 제약이 될 수도 있다

      interface E <A, B extends A> {}
    
      type u = E<string,'11'> // ok
      type u2 = E<number,11> // ok
      type u3 =E<number,'1'> // XX
    
  • 자주 사용되는 제약

| <T extends object> 모든 객체  |
| --- |
| <T extends any[]> 모든 배열 |
| <T extends (…args:any⇒any))> 모든 함수 |
| <T extends abstract new (…args:any)⇒ any> 생성자 타입 |
| <T extends keyof any> 속성의 키  // string | number | symbol |
- <T extends abstract new (…args:any)⇒ any>
    - T가 추상 클래스며 생성자 시그니처를 가져야하고, 어떤 타입의 인수도 받을 수 있고, 어떤 타입의 값도 반환할 수 있는 값으로 제한한다.
    - …으로 전개 구문 사용한 이유는 매개변수가 많을 경우를 대비.
  • 제네릭에서 자주하는 실수

      interface VO {
          value:any;
      }
      const returnVO = <T extends VO>():T=>{
          return {value:'text'}
      }
      // error T는 정확히 VO가 아니라 VO에 대입할 수 잇는 모든 타입을 의미한다.
      // 다시 말하면 {value:'text', name:'zzz', age:5} 같은 값도 T가 될 수 있음.
    
      const re = <T extends number>():T=>{
          return 3
      }
      // error도 마찬가지 T에 1,2,3,4,5,... 가 다 들어갈 수 있는데
      // 1,2,3,4,5,...,와 3은 같지 않으니까 error
      // re<5>() => T는 5인데 return은 3이니까
    
    • 천천히 생각해보면 T extends VO가 의미하는 것은 VO에 대입할 수 있는 모든 값들이 T가 될 수 있다는 것을 의미한다.
    • ts에서 대입은 좁은 타입에서 넓은 타입으로 이루어지니까 {value:’123’, age:3}같은 좁은 타입이 넓은 VO타입에 대입될 수 있는 것이다.

      function onlyBoolean<T extends boolean>(arg:T = false): T {
        return arg;
      } // error
      
      function onlyBoolean<T extends boolean>(arg:T = false as any): T {
        return arg;
      } // ok
      
      function onlyBoolean<T extends boolean>(arg:T=false as never) :T{
        return arg;
      }// ok
      
      // T extends boolean에서 
      // T는 true, false, never가 될 수 있다.
      // 만약에 never가 들어오면
      
    • 더 좋은 해결 방법

      • 제네릭을 쓰지말자
      • 원시값 타입만 사용한다면 대부분 제네릭이나 제약을 걸지 않아도 된다.
      function onlyBoolean(arg:true|false=true):true|false{
        return arg
      }
      
      function onlyBooleana(arg:boolean=true):boolean{
        return arg
      }
      
      interface VO {
        value:any;
      }
      
      const f = () : VO=>{
        return {value:'test'}
      }
      

2.15 조건문과 비슷한 컨디셔널 타입

  • 조건에 따라 다른 타입이 되는 타입을 컨디셔널 타입이라고 한다
  • extends 연산자를 이용해 삼항연자가 처럼 사용된다
    • 특정 타입 extends 다른 타입 ? 참일 때 타입 : 거짓일 때 타입
  • extends해야 조건을 만족하는 것은 아니고, 대입 관계를 따진다.
type A1 = string;
type B1 = A1 extends string ? number : boolean; // number 타입

type A2 = number ;
type B2 = A2 extends string ? number : boolean; // boolean 

type Start = string | number;
type New = Start extends string | number ? Start[] : never;

let n: New = ['hi']
n = [123]

type New = Start[] // 이랑 뭐가 달라?  never를 꼭 분리해 주고 싶을때!

// strArr 와 stringArr는 never 포함 여부에 따라 다름!
type strArr = string[] 
type Choose<T> = T extends string ? string[] : never;
type stringArr = Choose<string>
type Never = Choose<number>
  • 매핑된 객체 타입에서 키가 never면 해당 속성은 제거된다
    • 이를 이용해서 특정 value의 타입만 필터링 할 수 있다!
type OmitByType<O,T> = {
    [K in keyof O as O[K] extends T ? never :K] : O[K];
};

type Result = OmitByType<{
    name:string;
    age:number;
    married:boolean;
    rich:boolean
}, boolean>;

const A : Result = {name:'cccc',age:1231231231} // 대박
  • 당연히 삼항연산자처럼 중첩해서 사용할 수 있다.
type C<A> = A extends string 
? string[] 
: A extends boolean ? boolean[]:never;
// string이면 string[] boolean이면 boolean[] 그것도 아니면 never
  • 인덱스 접근 타입으로 컨디셔널 타입 표현하기
type A1 = string;
type B1 = A1 extends string ? number : boolean;
type B2 = {
    't':number;
    'f':boolean;
}[A1 extends string ?'t':'f']

2.15.1 컨디셔널 타입 분배법칙

  • string | number 타입이 있는데 string[] 타입을 얻고 싶다면 어떻게 할까
  • 어떻게 다른 값이 나올까?
  • 검사하려는 타입이 제네릭이면서 유니언 타입이면 분배법칙이 실행된다.

      type Start = string | number;
    
      type wrongResult = Start extends string ? Start[]: never;
      // never 타입이됨.
    
      type Result<Key> = Key extends string ? Key[] : never;
      let n : Result<Start> = ['hi'];
      let n : Result<Start> = [123]; // error
    
      // Result<string | number>
      // Result<string> | Result<number>
    
      // Result<Key:string> = Key extends string ? Key[] : never;
      // Result<Key:number> = Key extends string ? Key[] : never;
    
  • boolean에 분배법칙이 적용될 때는 조심하자

    • boolean은 애초에 true | false의 유니온 집합이다

      type Start = string | number | boolean;
      type Result<Key> = Key extends string | booelan ? Key[]:never;
      let n : Result<Start> = ["hi"]; 
      // let n:string[] | false[] | true[]
      
  • 분배법칙 막기

    • 배열로 제네릭을 감싸면 막아짐

      type IsString<T> = T extends string ? true : false;
      type Result = IsString<'hi'|3>; 
      // Result는 boolean
      
      type IsString<T> = [T] extends [string] ? true : false;
      type Result = IsString<'hi'|3>;  // false
      
  • never도 분배법칙의 대상이다.

    • 기본적으로 never면 never extends 타입은 항상 참이라 true가 나와야하지만
    • never도 분배되면서 공집합이라 아무것도 실행되는 게 없어서 never가 나온다.

      type R<T> = T extends string ? true : false;
      type RR = R<never>; // type RR = never
      
    • never의 분배법칙을 막아보자

      • never도 분배법칙을 막을 수 있네!
      type R<T> = [T] extends [string] ? true : false;
      type RR = R<never>; // true!!
      
    • 주의사항

      • 제네릭과 컨디셔널 타입을 함께 쓸때는 분배법칙을 조심하자.
      function test<T>(a:T){
        type R<T> = T extends string ? T :T;
        const b:R<T> = a;
      }
      
      // 타입스크립트는 제네릭이 들어 있는 컨디셔널 타입의 경우 판단을 뒤로 미룬다
      // type R<T> = T extends string ? T :T;에서 R<T>는 무조건 T 타입을 가진다고
      // 생각하겠지만 타입스크립트는 팓단을 뒤로 미루기 때문에
      // b: R<T> = a할때 a는 T라고만 생각하고 R<T>가 T라고 확신하지 못해 에러가 발생함.
      
      function test<T>(a:T){
        type R<T> = [T] extends string ? T :T;
        const b:R<T> = a;
      }
      // []로 제네릭을 감싸면 지금 즉시 타입 판단을 하라고 알려줌
      // 이렇게 하면 error가 없어진다.
      

results matching ""

    No results matching ""