작은 도서관

구조적 서브타이핑

특정한 개체의 멤버만으로 타입을 관계시키는 방식.

type Person = {
	name: string,
    	age: number
}

let x: Person;

let y = {
	name: "Siyoun",
	age: 21
}

위처럼 Person타입의 x와 객체 y는 동일한 타입을 갖고 있어 서로 호환된다.

이는 y의 추론된 타입이 { name: string, age: number }이기 때문이다.

만약 어떤 새가 오리처럼 걷고, 헤엄치고, 꽥꽥거리는 소리를 낸다면 나는 그 새를 오리라고 부를 것이다.
type Student = Person & {
	major: string
}

const s1: Student = {
	name: "Siyoun",
    	age: 21,
    	major: "CE"
}

const s2 = {
	name: "Siyoun",
    	age: 21,
    	major: "CE"
}

function greeting(target: Person) {
	console.log(target.name + target.age);
}

명목적 서브타이핑을 사용하는 경우, s2는 Person을 상속받지 않았으므로 Person의 멤버를 전부 갖고 있더라도 greeting함수의 인자로 사용할 수 없다.

Fresh Literal

typescript의 객체는 신선도(freshness)라는 개념이 있는데, 만약 이 신선도가 "fresh"한 상태라면 타입 호환성을 제공하지 않는다.

모든 object literal은 초기에 "fresh"한 상태를 가지며, 만약 해당 객체의 타입을 단언하거나, 타입이 추론된다면 해당 상태를 잃게된다.

 

만약 함수에 인자로 object literal을 바로 전달하는 경우 fresh한 상태로 전달되며, 대상 인자의 멤버를 전부 갖고 있더라도 호환되지 않는다.

function greeting(target: Person) {
	console.log(target.name + target.age);
}

// 함수 안의 객체는 fresh 상태를 가짐
greeting({ name: "Siyoun", age: 21 });

타입 호환을 임의로 지정하기

fresh object에 대해 타입 호환 허용하기 - index signature 사용

특정 타입에 대해 어떤 멤버던 추가할 수 있도록 index signature를 지정해준다면 fresh object도 타입 호환이 허용된다.

type Person = {
	name: string,
    	age: number,
        [index: string]: any
}

function greeting(target: Person) {
	console.log(target.name + target.age);
}

greeting({ name: "siyoun", age: 21 });

위 코드의 경우 greeting 함수의 인자로 fresh object를 넘에도 실행이 되는건 물론, Person에 없는 멤버가 추가로 있어도 정상적으로 실행된다.

모든 freshness에 대해 타입 호환 허용하지 않기 - branded type 사용

type Brand<K, T> = K & { __brand: T };
type Person = Brand<{
	name: string;
    	age: number;
}, "Person">

const s1 = {
	name: "siyoun",
    	age: 21,
        major: "CE"
}

이 경우 Person과 s1은 멤버가 갖더라도 다른 타입으로 취급되어 fresh literal과 상관없이 서로 호환되지 않는다.

레퍼런스

https://toss.tech/article/typescript-type-compatibility

'개발 > 칼럼읽기' 카테고리의 다른 글

[개념] 토큰과 세션  (0) 2023.07.17
profile

작은 도서관

@Flrea

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!