사용자 정의 타입가드, 타입 호환

soohyunee·2023년 5월 29일
0

TypeScript

목록 보기
20/20
post-thumbnail

1. 사용자 정의 타입가드 (User-Defined Type Guard)

interface Developer {
	name: string;
	skill: string;
}

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

function introduce(): Developer | Person {
	return {
		name: 'Tony',
		age: 33,
		skill: 'Iron Making',
	};
}

let tony = introduce();
function isDeveloper(target: Developer | Person): target is Developer {
	return (target as Developer).skill !== undefined;
}

if (isDeveloper(tony)) {
	tony.skill;
} else {
	tony.age;
}
  • 타입 가드 패턴 중 많이 사용하는 패턴 중 하나
  • 함수 이름은 is해당타입 처럼 is를 붙이고, 해당 타입인지 아닌지를 나타내는 함수를 작성
  • 파라미터(target)로 타입들을 유니언 타입으로 동일하게 받을 수 있게 함
  • is 키워드 : 넘겨받은 파라미터가 해당 타입인지를 반환
  • 반환값을 as 키워드를 사용하여 해당 타입으로 가정하고, 해당 타입만의 속성이 있다면 해당 타입이라고 취급
  • is 로직을 통과하면 해당 타입인지 아닌지 구분을 해주므로, 조건문을 통해 제공되는 속성값을 다르게 함
  • 타입가드를 정의하고 로직을 통해 boolean 값을 반환하여 해당 타입인지 구분을 함
  • 해당 타입이라면 속성에 접근을 하고, 아니라면 다른 타입의 속성에 접근할 수 있게 해줌
  • 각각의 필요한 속성에 접근할 수 있는 타입가드를 이용한 타입 구분 방식

2. 타입 호환 (Type Compatibility)

interface Person {
	name: string;
}

class Developer {
	name: string;
}

let i: Person;
i = new Developer(); // 정상 동작
  • 타입 호환 : 타입스크립트 코드에서 특정 타입이 잘 맞는지를 의미, 타입스크립트가 코드를 해석해나가는 과정에서 두 개의 타입이 서로 호환이 되는지를 점검하는 것
  • 타입스크립트 관점에서는 타입이 정의되어있는 속성을 갖고 타입이 호환되는 지를 점검
  • 구조적 타이핑 : 코드 구조 관점에서 타입이 호환되는지 여부 판단

interface Developer {
	name: string;
	skill: string;
}

interface Person {
	name: string;
}

let developer: Developer;
let person: Person;

person = developer
developer = person; // 에러 발생
  • 오른쪽 타입이 왼쪽 타입에 호환되려면 더 많은 속성을 갖거나 구조적으로 더 커야함
  • 구조적으로 더 큰 타입에서는 더 작은 타입을 지원할 수 없음
  • 항상 타입이 맞지 않아도 구조적으로 내부에 존재하고 있는 속성과 타입에 대한 정의들에 대해서만 비교를 함

let add = (a: number) => {
	// ...
};

let sum = (a: number, b: number) => {
	// ...
};

sum = add;
add = sum; // 에러 발생
  • 함수는 파라미터의 타입이 많은 쪽이 구조적으로 크고, 왼쪽에 있어야 함
  • 함수 내부적으로 파라미터 혹은 반환값이 더 많거나 많은 타입들을 가져가면 그 함수는 구조적으로 큼
  • 더 넓은 범위의 함수가 더 작은 범위의 함수를 호환할 수 있음
interface Empty<T> {}

let empty1: Empty<string>;
let empty2: Empty<number>;

empty1 = empty2;
empty2 = empty1;

interface NotEmpty<T> {
	data: T;
}

let notempty1: NotEmpty<string>;
let notempty2: NotEmpty<number>;

notempty1 = notempty2; // 에러 발생
notempty2 = notempty1; // 에러 발생
  • 구조적으로 비어있으면 어떠한 값이 들어가더라도 서로 동일한 타입이라고 간주하고, 호환이 됨
  • 구조적으로 제네릭에 의해 값이 바뀐다고 하면, 구조적인 타입의 차이가 생김
  • 즉, 동일한 속성은 있지만 타입은 달라지므로 서로 호환이 되지 않음

참고 : 타입스크립트 입문 - 기초부터 실전까지

profile
FrontEnd Developer

0개의 댓글