교차 타입은 여러 타입을 하나로 결합
기존 타입을 합쳐 필요한 모든 기능을 가진 하나의 타입을 얻을 수 있다.
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);
유니언 타입
유니언 타입은 값이 여러 타입 중 하나임을 설명
세로 막대 (|)로 각 타입을 구분하여 number | string | boolean은 값의 타입은 number, string 혹은 boolean이 될 수 있음을 나타낸다.
/**
* 문자열을 받고 왼쪽에 "padding"을 추가합니다.
* 만약 'padding'이 문자열이라면, 'padding'은 왼쪽에 더해질 것입니다.
* 만약 'padding'이 숫자라면, 그 숫자만큼의 공백이 왼쪽에 더해질 것입니다.
*/
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(); // 오류
property 'swim' does not exist on type 'Bird | Fish'.
Property 'swim' does not exist on type 'Bird'.(2
타입 가드와 차별 타입
유니언 타입의 모든 구성 성분을 가지고 있다고 보장되는 멤버에만 접근하려면
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)
TypeScript에는 타입 가드라는 것이 있다.
타입 가드는 스코프 안에서의 타입을 보장하는 런타임 검사를 수행한다는 표현식
타입 서술어 사용하기
function isFish(pet: Fish | Bird): pet is Fish {
return (pet as Fish).swim !== undefined;
}
pet is Fish는 이 예제에서의 타입 서술어
서술어는 parameterName is Type 형태이고, parameterName는 반드시 현재 함수 시그니처의 매개변수 이름이어야 한다.
if (isFish(pet)) {
pet.swim();
}
else {
pet.fly();
}
function isFish(pet: Fish | Bird): boolean {
return (pet as Fish).swim !== undefined;
}
in 연산자 사용하기 (Using the in operator)
in 연산자는 타입을 좁히는 표현으로 작용.
n in x 표현에서, n은 문자열 리터럴 혹은 문자열 리터럴 타입이고 x는 유니언 타입
function move(pet: Fish | Bird) {
if ("swim" in pet) {
return pet.swim();
}
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를 타입 가드로 인식하기 때문에 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 타입 가드의 두 가지 다른 형식
typeof v === "typename"
typeof v !== "typename"
※ 여기서 typename은 "number", "string", "boolean" 그리고 "symbol"
TypeScript에서 위에 없는 다른 문자열과 비교하는 것을 막지는 않지만, 타입 가드의 표현식으로 인지되지 않는다.
컴파일러가 null이나 undefined를 제거할 수 없는 경우, 타입 단언 연산자를 사용하여 수동으로 제거할 수 있다
방법 :구문은 !를 후위 표기. identifier!는 null과 undefined를 identifier의 타입에서 제거
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");
}
예제는 중첩 함수를 사용. 왜냐하면 컴파일러가 중첩 함수안에서는 null을 제거할 수 없기 때 (즉시-호출된 함수 표현은 예외).
특히 외부 함수에서 호출될 경우, 중첩 함수에 대한 모든 호출을 추적할 수 없기 때문
타입 별칭 (Type Aliases)
별칭은 실제로 새로운 타입을 만드는 것이 아니라-그 타입을 나타내는 새로운 이름 을 만드는 것.
원시 값의 별칭을 짓는 것은 문서화의 형태로 사용할 수 있지만, 별로 유용하지 않는다.
type Name = string;
type NameResolver = () => string;
type NameOrResolver = Name | NameResolver;
function getName(n: NameOrResolver): Name {
if (typeof n === "string") {
return n;
}
else {
return n();
}
}
인터페이스처럼, 타입 별칭은 제네릭이 될 수 있다 - 타입 매개변수를 추가하고 별칭 선언의 오른쪽에 사용
type LinkedList<T> = T & { next: LinkedList<T> };
interface Person {
name: string;
}
var people: LinkedList<Person>;
var s = people.name;
var s = people.next.name;
var s = people.next.next.name;
var s = people.next.next.next.name;
※ 타입 별칭을 선언의 오른쪽 이외에 사용하는 것은 불가
type Yikes = Array<Yikes>; /
인터페이스 vs. 타입 별칭 (Interfaces vs. Type Aliases)
대부분 동일한 기능을 제공하지만 한 가지 큰 차이점은 타입은 new properties 추가하기 위한 재할당이 불가능하지만
인터페이스는 항상 확장가능한다.
type Window = {
title: string
}
type Window = {
ts: import("typescript")
}
// Error: Duplicate identifier 'Window'.
소프트웨어의 이상적인 특징은 확장에 개방되어 있기 때문에, 가능하면 항상 타입 별칭보다 인터페이스를 사용해여 한다.
반면에, 만약 인터페이스로 어떤 형태를 표현할 수 없고 유니언이나 튜플 타입을 사용해야 한다면, 일반적으로 타입 별칭을 사용해야 한다.
문자열 리터럴 타입 (String Literal Types)
문자열 리터럴 타입은 문자열에 값을 정확하게 지정할 수 있게 해준다.
예제에서 문자열 리터럴 타입은 유니언 타입, 타입 가드, 그리고 타입 별칭과 잘 결합된다.
type Easing = "ease-in" | "ease-out" | "ease-in-out";
class UIElement {
animate(dx: number, dy: number, easing: Easing) {
if (easing === "ease-in") {
// ...
}
else if (easing === "ease-out") {
}
else if (easing === "ease-in-out") {
}
else {
// 오류! null이나 undefined를 전달하면 안됩니다
}
}
}
let button = new UIElement();
button.animate(0, 0, "ease-in");
button.animate(0, 0, "uneasy"); // 오류: "uneasy"는 여기서 허용하지 않습니다
타입은 오버로드를 구별하기 위해 같은 방법으로 사용할
function createElement(tagName: "img"): HTMLImageElement;
function createElement(tagName: "input"): HTMLInputElement;
// ... 더 많은 오버로드 ...
function createElement(tagName: string): Element {
// ... 이곳에 코드를 ...
}
링크 : https://www.typescriptlang.org/docs/handbook/advanced-types.html
교차 타입 (Intersection Types)
교차 타입은 여러 타입을 하나로 결합 기존 타입을 합쳐 필요한 모든 기능을 가진 하나의 타입을 얻을 수 있다.
유니언 타입
유니언 타입은 값이 여러 타입 중 하나임을 설명 세로 막대 (|)로 각 타입을 구분하여 number | string | boolean은 값의 타입은 number, string 혹은 boolean이 될 수 있음을 나타낸다.
유니언 타입을 값으로 가지고 있으면, 유니언에 있는 모든 타입에 공통인 멤버에만 접근할 수 있다.
타입 가드와 차별 타입
유니언 타입의 모든 구성 성분을 가지고 있다고 보장되는 멤버에만 접근하려면
사용자-정의 타입 가드 (User-Defined Type Guards)
TypeScript에는 타입 가드라는 것이 있다. 타입 가드는 스코프 안에서의 타입을 보장하는 런타임 검사를 수행한다는 표현식
타입 서술어 사용하기
pet is Fish는 이 예제에서의 타입 서술어 서술어는 parameterName is Type 형태이고, parameterName는 반드시 현재 함수 시그니처의 매개변수 이름이어야 한다.
in 연산자 사용하기 (Using the in operator)
in 연산자는 타입을 좁히는 표현으로 작용. n in x 표현에서, n은 문자열 리터럴 혹은 문자열 리터럴 타입이고 x는 유니언 타입
typeof 타입 가드 (typeof type guards)
TypeScript는 typeof를 타입 가드로 인식하기 때문에 typeof x === "number"를 함수로 추상할 필요가 없다. 즉 타입 검사를 인라인으로 작성할 수 있다
typeof 타입 가드의 두 가지 다른 형식
※ 여기서 typename은 "number", "string", "boolean" 그리고 "symbol" TypeScript에서 위에 없는 다른 문자열과 비교하는 것을 막지는 않지만, 타입 가드의 표현식으로 인지되지 않는다.
instanceof 타입 가드 (instanceof type guards)
instanceof의 오른쪽은 생성자 함수여야 하며 다음의 과정으로 좁혀가며 타입가드 수행
널러블 타입 (Nullable types)
null과 undefined --strictNullChecks 플래그 선언하고 변수를 선언할 때, 자동으로 null이나 undefined를 포함하지 않는다. 대신에 유니언 타입을 사용하여 명시적으로 포함해야 한다.
TypeScript는 JavaScript와 맞추기 위해 null과 undefined를 다르게 처리
선택적 매개변수와 프로퍼티
--strictNullChecks를 적용하면, 선택적 매개변수가 "| undefined"를 자동으로 추가
선택적 프로퍼티도 마찬가지
타입 가드와 타입 단언 (Type guards and type assertions)
널러블 타입이 유니언으로 구현되기 때문에, null을 제거하기 위해 타입 가드를 사용할 필요가 있다
컴파일러가 null이나 undefined를 제거할 수 없는 경우, 타입 단언 연산자를 사용하여 수동으로 제거할 수 있다 방법 :구문은 !를 후위 표기. identifier!는 null과 undefined를 identifier의 타입에서 제거
예제는 중첩 함수를 사용. 왜냐하면 컴파일러가 중첩 함수안에서는 null을 제거할 수 없기 때 (즉시-호출된 함수 표현은 예외). 특히 외부 함수에서 호출될 경우, 중첩 함수에 대한 모든 호출을 추적할 수 없기 때문
타입 별칭 (Type Aliases)
별칭은 실제로 새로운 타입을 만드는 것이 아니라-그 타입을 나타내는 새로운 이름 을 만드는 것. 원시 값의 별칭을 짓는 것은 문서화의 형태로 사용할 수 있지만, 별로 유용하지 않는다.
인터페이스처럼, 타입 별칭은 제네릭이 될 수 있다 - 타입 매개변수를 추가하고 별칭 선언의 오른쪽에 사용
교차 타입
※ 타입 별칭을 선언의 오른쪽 이외에 사용하는 것은 불가
인터페이스 vs. 타입 별칭 (Interfaces vs. Type Aliases)
대부분 동일한 기능을 제공하지만 한 가지 큰 차이점은 타입은 new properties 추가하기 위한 재할당이 불가능하지만 인터페이스는 항상 확장가능한다.
소프트웨어의 이상적인 특징은 확장에 개방되어 있기 때문에, 가능하면 항상 타입 별칭보다 인터페이스를 사용해여 한다. 반면에, 만약 인터페이스로 어떤 형태를 표현할 수 없고 유니언이나 튜플 타입을 사용해야 한다면, 일반적으로 타입 별칭을 사용해야 한다.
문자열 리터럴 타입 (String Literal Types)
문자열 리터럴 타입은 문자열에 값을 정확하게 지정할 수 있게 해준다. 예제에서 문자열 리터럴 타입은 유니언 타입, 타입 가드, 그리고 타입 별칭과 잘 결합된다.
타입은 오버로드를 구별하기 위해 같은 방법으로 사용할
숫자 리터럴 타입 (Numeric Literal Types)
명시적으로 작성되는 경우는 거의 없지만, 이슈를 좁히고 버그를 잡는데 유용할 수 있다.
열거형 멤버 타입 (Enum Member Types)
열거형 멤버는 모든 멤버가 리터럴로-초기화될 때 타입을 가집니다. 싱글톤 타입을 이야기 할때 여기서는 열거형 멤버 타입과 숫자/문자열 리터럴 타입을 얘기하지만 대부분 사람들은 싱글톤타입을 리터럴과 동의어(interchagable)처럼 사용한다.