-
[TypeScript] 고급타입-1TypeScript 2022. 9. 21. 23:26728x90
교차 타입(Intersection Types)
교차 타입은 여러 타입을 하나로 결합합니다.
Person & Serializable & Loggable
은Person
,Serializable
,Loggable
타입의 모든 멤버를 갖습니다.아래는 믹스인을 만드는 간단한 예제입니다.
function extend<First, Second>(first: First, second: Second): First & Second { const result: Partial<First & Second> = {}; for (const prop in first) { if (first.hasOwnProperty(prop)) { (result as First)[prop] = first[prop]; } } for (const prop in second) { if (second.hasOwnProperty(prop)) { (result as Second)[prop] = second[prop]; } } return result as First & Second; } class Person { constructor(public name: string) { } } interface Loggable { log(name: string): void; } class ConsoleLogger implements Loggable { log(name) { console.log(`Hello, I'm ${name}.`); } } const jim = extend(new Person('Jim'), ConsoleLogger.prototype); jim.log(jim.name);
유니언 타입(Union Types)
유니언 타입은 교차 타입과 밀접하게 관련되어 있지만, 매우 다르게 사용됩니다.
/** * 문자열을 받고 왼쪽에 "padding"을 추가합니다. * 만약 'padding'이 문자열이라면, 'padding'은 왼쪽에 더해질 것입니다. * 만약 'padding'이 숫자라면, 그 숫자만큼의 공백이 왼쪽에 더해질 것입니다. */ function padLeft(value: string, padding: any) { if (typeof padding === "number") { return Array(padding + 1).join(" ") + value; } if (typeof padding === "string") { return padding + value; } throw new Error(`Expected string or number, got '${padding}'.`); } padLeft("Hello world", 4); // " Hello world"를 반환합니다. let indentedString = padLeft("Hello world", true); // 컴파일 타임에 통과되고, 런타임에 오류.
만약
padding
이any
타입이 아닌number
와string
으로 이루어진 유니언 타입이라면 컴파일중에 오류가 발생하게 됩니다.function padLeft(value: string, padding: string | number) { // ... } let indentedString = padLeft("Hello world", true); // 컴파일 중에 오류
유니언 타입의 핵심은 유니언에 있는 모든 타입에 공통 멤버에만 접근이 가능하다는 것 입니다.
interface Bird { fly(); layEggs(); } interface Fish { swim(); layEggs(); } function getSmallPet(): Fish | Bird { // ... } let pet = getSmallPet(); pet.layEggs(); // 성공 pet.swim(); // 오류
getSmallPet
은Fish
또는Bird
멤버일 수 있는데swim
메서드는Bird
멤버에선 사용 불가,fly
메서드는Fish
멤버에서 사용이 불가합니다. 따라서TypeScript
는 확실히 사용 가능한layEgg
메서드만 호출할 수 있다고 이해하면 될 것 같습니다.타입 가드와 차별 타입(Type Guards and Differentiating Types)
유니언 타입은 값의 타입이 겹쳐질 수 있는 상황을 모델링하는데 유용합니다.
양쪽 멤버에 공통으로 존재하지 않는 메서드에 접근하기 위해서는 타입 단언
as
를 사용해야 합니다.let pet = getSmallPet(); if ((pet as Fish).swim) { (pet as Fish).swim(); } else if ((pet as Bird).fly) { (pet as Bird).fly(); }
사용자-정의 타입 가드(User-Defined Type Guards)
위와같이 사용하면 if문마다 타입 단언을 사용해야합니다. TypeScript는 타입 가드를 통해서 스코프 안에서의 타입을 보장하는 런타임 검사를 수행하는 방법이 존재합니다.
타입 서술어 사용하기(Using type predicates)
타입 가드를 정의하기 위해서는 반환 타입이
타입 서술어
인 함수를 정의해야 합니다.function isFish(pet: Fish | Bird): pet is Fish { return (pet as Fish).swim !== undefined; } // 이제 'swim'과 'fly'에 대한 모든 호출은 허용됩니다 if (isFish(pet)) { pet.swim(); } else { pet.fly(); }
pet is Fish
는 타입 서술어입니다.- 타입 서술어 :
parameterName is Type
형태이고,parameterName
은 반드시 함수 시그니처의 매개변수 이름이어야 합니다.
isFish
가 호출되면 TypeScript는 그 변수를 특정 타입으로 제한합니다.따라서 if문에선 반드시
Fish
임을 뜻하고, else문에선 반드시Fish
가 아니므로Bird
를 뜻합니다.in 연산자 사용하기 (Using the in operator)
function move(pet: Fish | Bird) { if ("swim" in pet) { return pet.swim(); } return pet.fly(); }
in
연산자는 타입을 좁히는 용도로 사용됩니다.in
연산자의앞
엔문자열 리터럴
이거나문자열 리터럴 타입
이 올 수 있습니다.in
연산자의뒤
에는유니언 타입
이 올 수 있습니다.“swim” in pet
에서pet
은swim
을 가지는 타입으로 좁혀지므로Fish
가 되고, 그게 아니면Bird
가 되기때문에 마지막에return pet.fly()
가 가능해집니다.typeof 타입 가드 (typeof type guards)
타입 서술어를 이용하면 아래와 같습니다.
function isNumber(x: any): x is number { return typeof x === "number"; } function isString(x: any): x is string { return typeof x === "string"; } function padLeft(value: string, padding: string | number) { if (isNumber(padding)) { return Array(padding + 1).join(" ") + value; } if (isString(padding)) { return padding + value; } throw new Error(`Expected string or number, got '${padding}'.`); }
하지만 매번 저런 함수를 정의하는 것은 너무 귀찮은데요,
TypeScript는
typeof x === ‘number’
를 함수로 추상하지 않아도 됩니다.function padLeft(value: string, padding: string | number) { if (typeof padding === "number") { return Array(padding + 1).join(" ") + value; } if (typeof padding === "string") { return padding + value; } throw new Error(`Expected string or number, got '${padding}'.`); }
타입 가드는
!==
도 사용 가능합니다.typeof v === ‘typename’
에서typename
는 반드시number,
string
,boolean
그리고symbol
이어야 합니다. 그 외의 타입은 타입 가드 표현식으로 인식되지 않습니다.instanceof 타입 가드 (instanceof type guards)
instanceof
타입 가드는 생성자 함수를 사용하여 타입을 좁히는 방식입니다.interface Padder { getPaddingString(): string } class SpaceRepeatingPadder implements Padder { constructor(private numSpaces: number) { } getPaddingString() { return Array(this.numSpaces + 1).join(" "); } } class StringPadder implements Padder { constructor(private value: string) { } getPaddingString() { return this.value; } } function getRandomPadder() { return Math.random() < 0.5 ? new SpaceRepeatingPadder(4) : new StringPadder(" "); } // 타입은 'SpaceRepeatingPadder | StringPadder' 입니다 let padder: Padder = getRandomPadder(); if (padder instanceof SpaceRepeatingPadder) { padder; // 타입은 'SpaceRepeatingPadder'으로 좁혀집니다 } if (padder instanceof StringPadder) { padder; // 타입은 'StringPadder'으로 좁혀집니다 }
instanceof
의 오른쪽은 반드시 생성자 함수여야 합니다.TypeScript는 타입을 다음과 같이 좁힙니다
- 함수의
prototype
프로퍼티 타입이any
가 아닌 경우 - 타입의 생성자 시그니처에서 반환된 유니언 타입일 경우
널러블 타입(Nullable types)
TypeScript는
null
과undefined
를 다르게 처리합니다.null
과undefined
는 어떤 타입에든 할당할 수 있다고 간주됩니다. 따라서 방지하고 싶어도 어떤 타입에 할당되는 것을 방지 할 수 없습니다.따라서 이건
—strictNullChecks
플래그로 해결을 합니다.—strictNullChecks : true
면 변수 선언 시null
,undefined
를 포함하지 않습니다.let s = "foo"; s = null; // 오류, 'null'은 'string'에 할당할 수 없습니다 let sn: string | null = "bar"; sn = null; // 성공 sn = undefined; // 오류, 'undefined'는 'string | null'에 할당할 수 없습니다. // null과 undefined를 다르게 간주하기 때문에
선택적 매개변수와 프로퍼티(Optional parameters and properties)
--strictNullChecks
를 적용하면, 선택적 매개변수?
가| undefined
를 자동으로 추가합니다function f(x: number, y?: number) { return x + (y || 0); } f(1, 2); f(1); f(1, undefined); f(1, null); // 오류, 'null'은 'number | undefined'에 할당할 수 없습니다
타입 가드와 타입 단언(Type guards and type assertions)
null
타입이 유니언으로 구현되기 대문에,null
을 제거하려면 타입 가드를 사용해야 합니다.function f(sn: string | null): string { if (sn == null) { return "default"; } else { return sn; } }
조금더 간단하게 아래와 같이도 사용 가능합니다.
function f(sn: string | null): string { return sn || "default"; }
타입 단언 연산자
!
를 후위에 표기하여 null과 undefined 타입을 제거할 수 있습니다.function broken(name: string | null): string { function postfix(epithet: string) { return name.charAt(0) + '. the ' + epithet; // 오류, 'name'은 아마도 null 입니다 } name = name || "Bob"; return postfix("great"); } function fixed(name: string | null): string { function postfix(epithet: string) { return name!.charAt(0) + '. the ' + epithet; // 성공 } name = name || "Bob"; return postfix("great"); }
728x90'TypeScript' 카테고리의 다른 글
[TypeScript] 선언병합 (1) 2022.09.23 [TypeScript] 고급타입 - 2 (1) 2022.09.22 [TypeScript] 제네릭 (0) 2022.09.17 [TypeScript] 열거형 (1) 2022.09.16 [TypeScript] 클래스 (0) 2022.09.15 - 타입 서술어 :