ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 02-타입호환성
    Programming/Typescript 2021. 11. 22. 18:42
    반응형

    인프런 이재승 님 타입스크립트 강의를 듣고 여러 레퍼런스를 참고하여 정리한 내용입니다.

    1. 타입호환성

    타입 호환성은 어떤 타입을 다른 타른 타입으로 취급해도 되는지 판단하는 것이다.

    정적타입언어의 가장 중요한 역할은 타입호환성을 통해

    컴파일 타임에 호환되지 않는 타입을 찾아내는 것이다.

    어떤 변수가 다른 변수에 할당 가능하기 위해선 해당 변수의 타입이 다른쪽 변수 타입에 할당될 수 있어야 한다.

    타입이 가질 수 있는 값의 집합을 생각해야 한다.

    function func1(a: number, b: number | string) {
        const v1: number | string = a;
        const v2: number = b; // error
    }
    
    function func2(a: 1 | 2) {
        const v1: 1 | 3 = a; // error
        const v2: 1 | 2 | 3 = a;
    }

    func1을 살펴 보자. number와 string으로 이루어진 값들의 집합이 number만으로 이루어진 값들의 집합보다 더 크다.

    따라서 v1(더 큰 거)에 v2(더 작은 거)가 할당 가능하다.

    반대로 v2는 number로만 이루어진 값들의 집합(더 작은 거)에 number와 string으로 이루어진 값들의 집합(더 큰 거)을 할당하려고 하니 error가 발생한다.

    func2도 마찬가지다.

    즉, 타입스크립트에서 할당되는 변수의 타입의 범위가 할당하려는 변수의 타입의 범위보다 커야 할당이 가능하다.

    structural typing

    타입스크립트는 값 자체의 타입보다는 값이 가진 내부 구조에 기반해서 타입 호환성을 검사한다.

    이를 structural typing 이라고 한다.

    interface Person {
        name: string;
        age: number;
    }
    
    interface Product {
        name: string;
        age:  number;
    }
    
    const person: Person = { name: 'simi', age: 23 };
    const product: Product = person; // 할당 가능

    Person과 Product는 타입 이름이 다르지만

    그 내부의 구조가 같기 때문에 할당이 가능하다.

    인터페이스 A가 인터페이스 B로 할당 가능하기 위한 조건을 보면 아래와 같다.

    1. B에 있는 모든 필수 속성의 key가 A에도 존재해야 한다.
    2. 같은 속성 key에 대해 A의 속성이 B의 속성에 할당 가능해야 한다.

    즉, Product의 필수 키, name과 age가 Person에도 모두 존재하고, A의 속성들이 B의 속성들에 할당이 가능하기 때문에

    Person은 Product에 할당 가능하다.

    만일 Person에 age가 없거나

    // 1번 조건 불만족
    interface Person {
        name: string;
        // age: number;
    }
    
    interface Product {
        name: string;
        age:  number;
    }
    
    const person: Person = { name: 'simi' };
    const product: Product = person; // error

    Person의 age를 Product의 age에 할당할 수 없으면 에러가 발생한다.

    // 2번 조건 불만족
    interface Person {
        name: string;
        age: number;
    }
    
    interface Product {
        name: string;
        age:  string;
    }
    
    const person: Person = { name: 'simi', age: 23 };
    const product: Product = person; // error

    위 상황을 좀더 깊게 살펴보자.

    interface Person {
        name: string;
    }
    
    interface Product {
        name: string;
        age:  number;
    }
    
    const obj = { name: 'simi', age: '23', city: 'abc' };
    let person: Person = obj;
    let product: Product = obj; // error.

    obj는 Person에 할당 가능하다. name의 조건을 만족하기 때문이다. 뒤의 값은 상관 없다.

    Product는 불가하다. age의 타입이 다르기 때문이다.

    // 위 코드와 연결
    person = product;
    product = person // error

    인터페이스에 정의된 속성이 많을수록 더 많은 제약이 있다는 것이고, 값의 집합이 더 작아진다는 뜻이다.

    즉, Person은 Product보다 값의 집합이 크다.

    따라서 Person에 Product를 할당할 수는 있지만 그 반대는 불가능하다.

    선택 속성이 있을 때는?

    Person의 age가 선택 속성이어도 마찬가지로 Person의 범위가 Product의 범위보다 크다.

    interface Person {
        name: string;
        age?: number
    }
    
    interface Product {
        name: string;
        age:  number;
    }
    
    const obj = { name: 'simi' };
    let person: Person = obj;
    let product: Product = obj; // error.
    
    person = product;
    product = person // error

    복습해보자.

    interface Person {
        name: string;
        age: number;
        gender: string;
    }
    
    interface Product {
        name: string;
        age:  number | string;
    }
    
    const person: Person = {
        name: 'simi',
        age: 23,
        gender: 'male'
    }
    
    const product: Product = person; 

    Person의 속성 수가 더 많고, Product의 age 속성의 범위가 더 넓기 때문에 Product가 더 큰 범위를 갖고 있다.

    따라서 Person이 Product에 할당 가능하다. 반대는 불가하다.

    함수의 타입 호환성

    함수는 호출하는 시점에 문제가 없어야 할당 가능하다.

    함수 타입 A가 함수 타입 B로 할당 가능하기 위한 조건은 아래와 같다.

    1. A의 매개변수 개수가 B의 매개변수 개수보다 적어야 한다.
    2. 같은 위치의 매개변수에 대해 B의 매개변수가 A의 매개변수로 할당 가능해야 한다.
    3. A의 반환값은 B의 반환값으로 할당 가능해야 한다.
    type F1 = ( a: number, b: string) => string;
    type F2 = ( a: number, b: string | number) => string;
    type F3 = ( a: number) => string;
    type F4 = ( a: number) => number | string;
    let f1: F1 = (a, b) => `${a} ${b.length}`;
    let f2: F2 = (a, b) => `${a} ${b}`;
    let f3: F3 = a => `${a}`;
    let f4: F4 = a => (a < 10 ? a : 'too big');

    위와 같은 함수들이 정의되었다고 할 때,

    // 1번 조건 
    f1 = f3; // 가능
    f3 = f1; // error. f3의 매개변수가 f1의 매개변수보다 적다.

    f3(1)을 호출한다고 하면 f1의 파라미터 b에는 undefined가 전달되고, length를 찾을 수 없어 error.

    // 2번 조건
    f1 = f2; // 가능
    f2 = f1; // error. f2의 매개변수 b를 f1의 매개변수 b에 할당 할 수 없음. 

    f2(1, 1)을 호출한다고 하면 f1의 파라미터 b에는 1이 들어가고, length를 찾을 수 없어 error.

    // 3번 조건
    f4 = f3; // 가능
    f3 = f4; // error. f4의 반환값이 f3의 반환값보다 범위가 크기 때문에 할당 불가. 

    f3(1)을 호출하면 f4의 파라미터 a에 1이 들어가고 1이 반환되어

    f3(1).length;

    등과 같은 코드가 error가 되므로 할당이 불가능하다.

    반응형

    'Programming > Typescript' 카테고리의 다른 글

    04-생산성을높이는타입스크립트의기능  (0) 2021.11.22
    03-타입스크립트고급기능  (0) 2021.11.22
    01-타입정의하기  (0) 2021.11.22
    00. 타입스크립트 시작하기  (0) 2021.11.22
Designed by Tistory.