타입스크립트 교과서 2.12~2.14 정리

이수빈·2023년 9월 15일
0

Typescript

목록 보기
3/17
post-thumbnail

타입의 상속

  • 인터페이스는 extend 키워드를, type alias는 intersection연산자(&)를 통해 상속을 구현한다.

  • 타입별칭이 인터페이스를 상속할 수도 있고, 인터페이스가 타입별칭을 상속 할 수도 있다.

  • type Dog을 interface Cow로 확장했다. 하지만, Dog이 union일경우 확장이 불가하다.

  • 상속할때 부모의 type을 override 가능하다. (완전히 다른 타입이 아니라, 넓은범위에서 좁은범위의 대입이 가능한것임)


{
    interface Animal{
        name :string;
    }

    type Dog = Animal & {
        bark : string 
    }
  //bark는 string type, Dog type은 interface를 이용해 intersetion 함.

    type Cat = Animal | {
        meow : () =>void;
    }

    type Name = Cat['name']; // Animal에는 존재하지만, meow에는 존재x

    interface Cow extends Dog{
        bark : "hello"
    } // Dog의 bark 속성을 override => string to "hello"

    const ex : Cow = {
        bark : "hello",
    }
}

타입의 대입

  • 넓은타입부터 좁은타입으로 타입을 좁혀가며 대입하는게 원칙임

  • 배열이나 튜플에는 readonly 수식어를 붙일 수 있음 => readonly 수식어가 붙은게 더 넓은 타입임.

  • 타입이 넓다 좁다의 기준? => 경우의수로 판단하면.. readonly가 붙은게 더 좁은타입이 되는것이 아닌가?

  • 이 부분만 좀 예외적으로 생각하는 방식이 다른듯함.

 let a: readonly string[] = ["hi","readonly"];
 let b: string[] = ["hi","readonly"];

    a = b; // readonly string[] 타입에 string[] 대입 가능.

    b = a; //error =>  string[]타입에 readonly string[] 대입 불가능.

// 일반 배열
const arr1 = [1, 2, 3];
arr1.push(4); // 유효, 배열 수정 가능

// 읽기 전용 배열
const arr2: readonly number[] = [1, 2, 3];
arr2.push(4); // 에러, 읽기 전용 배열은 수정 불가능

  • 프로퍼티가 optional인 객체가 optional이지 않은 객체보다 넓은타입임

  • 배열과 다르게 객체에서는 속성에 readonly가 붙어도 대입가능함.

구조적 타이핑

  • TS에서는 모든 속성이 동일하면 객체의 이름이 다르더라도 동일한 타입으로 취급함.

  • B인터페이스는 A인터페이스가 되기 위한 모든 조건을 충족함 => 구조적 타이핑관점에서 A인터페이스라고 할 수 있음. A인터페이스는 안됨.

  • CopyArr는 객체타입인데도 숫자배열을 대입 할 수 있음. CopyArr 타입에 존재하는 모든 속성을 숫자배열이 갖고 있음.

  • 서로 구분하게 하려면 => 브랜드 속성같이 type을 구분 짓는 속성을 따로 추가하는 방법으로 방지가능.

 interface A{
        name: string;
    }

   interface B{
        name:string;
        age:number;
    }

    type Arr = number[];
    type CopyArr = {
        [key in keyof Arr] : Arr[key];
    }

    const copyArr:CopyArr = [1,3,4];

	// 브랜드속성 사용 예제
	interface IA{
        _type : "A" // 브랜드속성
        name: string;
    }

    interface IB{
        _type : "B"
        name:string;
    }

제네릭

변하는 부분(가변요소)를 제너릭으로 선언하자!

  • 제너릭을 사용해서 중복을 제거 할 수 있음.
{
    interface Zero{
        type : "human",
        race : "yellow",
        name : "zero",
        age : 28,
    }

    interface Nero{
        type:"human",
        race : "yellow",
        name : "nero",
        age : 32,
    }

    interface Person<N,A>{
        type :"human",
        race : "yellow",
        name : N,
        age : A,
    }

    interface Zero extends Person<"zero", 28>{}
    interface Nero extends Person<"nero", 32>{}
}
  • Array Type 예시 => Array 이면 key number이고 전부 string type으로 !!
  interface Array<T>{
        [key: number] :T,
        length : number,
    }
  • 함수 선언문이냐, 함수 표현식이냐에 따라 제너릭 표기 위치가 다름.
  • 객체, 클래스의 메서드에 따로 제너릭을 표기 할 수도 있음.
  • 타입 매개변수에 기본값을 사용 할 수 있음.
      const personFac = <N,A>(name:N, age:A)=>({
          type:"human",
          race: "yellow",
          name,
          age,
  
        
    })

    function personFactory<N,A>(name:N, age:A){
        return ({
            type:"human",
            race: "yellow",
            name,
            age,
        })
    }
  
    interface IPerson<N= string,A=number>{
        method : <B>(param:B) => void;
    }
  • 첫번째함수 (value23)에서 매개변수로 들어오는 type은 T[]임 => 그래서 hasValue의 value는 T타입으로 추론됨.

  • string[]가 파라미터로 들어왔다면, hasValue는 string type모두가 올 수 있음.

  • 상수형 타입으로 사용하려면(유니언처럼) => const 키워드를 제너릭에 붙혀주면 됨.

  • T값이 유니언타입으로 "a"|"b" 이렇게 선언됨.

    function values23<T>(initial :T[]){
        return {
            hasValue(value:T){
                return initial.includes(value),
            };
        }
    }
    
    const savedValues1 = values23(["a","b"]);
    savedValues1.hasValue("x"); // type string not error
  
 function values<const T>(initial :T[]){
        return {
            hasValue(value:T){
                return initial.includes(value),
            };
        }
    }

    const savedValues = values(["a","b"]);
    savedValues.hasValue("x"); // type => "a"|"b" error
  • 제너릭은 extends 키워드로 제약조건을 줄 수 있음

  • 제약이 걸리면 제약에 어긋나는 타입은 입력 할 수 없지만, 구체적인 타입은 입력 할 수 있음

  • 하나의 타입 매개변수가 다른 타입 매개변수의 제약이 될 수도 있음

        interface Example<A, B extends A>{
           a:A,
           b:B,
       }
    
       type Usecase1 = Example<string, number>; // error
       type Usecase2 = Example<string, "hi">; // ok
  • 많이 쓰이는 제약조건 (객체, 배열 , 함수, 생성자, 속성의 키)

  <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 type은 V0에 올 수 있는 모든 타입을 의미함. (즉 반례가 존재함)

  • 즉, 파라미터로 넘어오는 type이 정해져있는경우에는 제너릭을 사용하지않고 특정해서 type을 정해놓는게 좋음 + 원시값 타입을 사용할 때 대부분 제약을 걸지 않아도 되는 경우가 많음

    {
       interface V0{
           value: any;
       }
    
       const returnV0 = <T extends V0>():T => {
           return {value :'test'};
       } // {value:string , another : string}도 T가 될수있음.
     
       function onlyBoolean<T extends boolean>(arg:T = false):T{
           return arg;
       } //error T는 never일수도 있으므로 오류발생
       
        function onlyBoolean(arg: true |false = true){
           return arg;
       } //제너릭을 사용하지 않으면 오류해결
       
       const f = ():V0 =>{
       	return {value:"test"}
       }

}```

profile
응애 나 애기 개발자

0개의 댓글