TypeScript
Part.1
chapter.3
- 유니언 : 값에 허용된 타입을 두 개 이상의 가능한 타입으로 확장하는 것
- 내로잉 : 값에 허용된 타입이 하나 이상의 가능한 타입이 되지 않도록 좁히는 것
3.1 유니언 타입
1
let mathmatician = Math.random() > 0.5 ? undefined : "Mark Goldberg";
mathmatician
은undefined
이거나string
일 수 있다.- 이처럼 값이 정확히 어떤 타입인지는 모르지만 두 개 이상의 옵션 중 하나라는 것을 알고 있을 때, 이를 유니언이라고 한다.
- 이렇게 작성된
mathmatician
은string|undefined
로 표현된다. - 변수 위에 마우스를 가져가면 타입을 표시한다.
3.1.1 유니언 타입 선언
- 변수의 초깃값이 있더라도 변수에 대한 명시적 타입 애너테이션을 제공하는 것이 유용할 때 유니언 타입을 사용한다.
초깃값은
null
이지만 잠재적으로string
될 수 있음을 알린다.1 2 3 4 5
let thinker: string | null = null; if (Math.randon() > 0.5) { thinker = "Susanne"; // OK }
- 유니언 타입 선언의 순서는 중요하지 않으며
boolean|number
와number|boolean
은 같다.
3.1.2 유니언 속성
- 값이 유니언 타입일 때, 타입스크립트는 유니언으로 선언하여 접근이 가능한 메서드에만 접근할 수 있다.
유니언 외의 타입에 접근하려고 하면 타입 검사 오류가 발생한다.
1 2 3 4 5 6 7 8 9
let physicist = Math.random() > 0.5 ? "Marie" : 84; physicist.toString(); // OK physicist.toUpperCase(); // Error : Property 'toUpperCase' does not exist on type 'string | number ' // Property 'toUpperCase' does not exist on type 'number' physicist.toFiexd(); // Error : Property 'toFiexd' does not exist on type 'string | number' // Property 'toFiexd' does not exist on type 'string'
- 두 타입 모두 사용 가능한 메서드여야 한다.
- 이러한 여러 개의 타입 중 하나의 타입으로 된 값의 속성을 사용하려면, 값이 보다 구체적인 타입 중 하나라는 것을 타입스크립트에 알려야 한다.
- 이러한 과정을 내로잉이라고 부른다.
3.2 내로잉
- 값이 정의, 선언 혹은 이전에 유추된 것보다 더 구체적인 타입임을 코드에서 유추하는 것이다.
- 타입스크립트가 값의 타입이 이전에 알려진 것보다 더 좁혀졌다는 것을 알게 되면 값을 더 구체적인 타입으로 취급한다.
- 타입을 좁히는 데 사용하는 논리적 검사를 타입 가드라고 한다.
3.2.1 값 할당을 통한 내로잉
변수에 값을 할당하면 변수의 타입을 할당된 값의 타입으로 좁힌다.
1 2 3 4 5 6
let admiral: number | string; admiral = "Grace"; admiral.toUpperCase(); // OK : string admiral.toFixed(); // Error : Property 'toFixed' does not exist on type 'string'
- 변수에 유니언 타입 애너테이션이 명시되고 초깃값이 주어질 때 값 할당 내로잉이 작동한다.
- 타입스크립트는 변수가 나중에 유니언 타입으로 선언된 타입 중 하나의 값을 받을 수 있지만, 처음에는 초기에 할당된 값의 타입으로 시작한다.
1 2 3 4 5
let inventor: number | string = "Hedy"; inventor.toUpperCase(); // OK string inventor.toFixed(); // Error : Property 'toFixed' does not exist on type 'string'
3.2.2 조건 검사를 통한 내로잉
- 일반적으로 타입스크립트에서는 변수가 알려진 값과 같은지 확인하는
if
문을 통해 변수의 값을 좁히는 방법을 사용한다. - 변수가 알려진 값과 동일한 타입인지 확인한다.
1 2 3 4 5 6 7 8 9
let scientist = Math.random() > 0.5 ? "Rosalind" : 51; // scientist : number | string if (scientist === "Rosalind") { scientist.toUpperCase(); // OK } // scientist: string 타입 scientist.toUpperCase(); // Error : Property "toUpperCase" does not exist on type "string|number" // Property "toUpperCase" does not exist on type "number"
- 조건부 로직을 내로잉할 때, 만약 변수가 여러 타입 중 하나라면, 일반적으로 필요한 타입과 관련된 검사를 원할 것이다.
- 타입스크립트는 강제로 코드를 안전하게 작성할 수 있도록 돕는다.
3.2.3 typeof 검사를 통한 내로잉
- 직접 값을 확인해 타입을 좁히기도 하지만,
typeof
연산자를 사용할 수도 있다. 타입스크립트의
if
문에서 타입이 무엇인지 확인한다.1 2 3 4 5
let researcher = Math.random() > 0.5 ? "Rosalind" : 51; if (typeof researcher === "string") { researcher.toUpperCase(); // OK : string }
!
를 사용한 논리적 부정과else
문도 잘 작동한다.1 2 3 4 5
if (!(typeof researcher === "string")) { reseacher.toFixed(); // OK : number } else { researcher.toLowerCase(); // OK : string }
- 삼항 연산자를 이용해 다시 작성할 수 있다.
1 2 3
typeof researcher === "string" ? researcher.toUpperCase(); // OK : string : researcher.toFixed(); // OK : number
3.3 리터럴 타입
- 원시 타입 값 중 어떤 것이 아닌 특정 원싯값으로 알려진 타입이다.
- 원시 타입
string
은 존재할 수 있는 모든 문자열의 집합을 나타내지만, 리터럴 타입인Hypatia
는 하나의 문자열만 나타낸다. - 만약 변수를 선언하고 직접 리터럴 값을 할당하면, 타입스크립트는 해당 변수에 할당된 리터럴 값으로 유추한다.
타입 애너테이션에서는 리터럴과 원시 타입을 섞어서 사용할 수 있다.
1 2 3 4 5 6 7 8 9
let lifespan: number | "ongoing" | "uncertain"; lifespan = 89; // OK lifespan = "ongoing"; // OK lifespan = true; // Error : Type 'true' is not assignable to 'number|"ongoing"|"uncertain"' lifespan = "hi"; // Error : Type '"hi"' is not assignable to 'number|"ongoing"|"uncertain"'
1 2 3 4
boolean : true | false null 과 undefined 는 자기자신만을 리터럴 값으로 가진다. number: 0|1|2...|0.1|0.2|... string: ""|"a"|"b"|"c"|...|"aa"|"ab"|..
3.3 리터럴 할당 가능성
0과 1은 동일한 원시 타입을 갖고 있지만 다른 리터럴 타입이기 때문에 할당할 수 없다.
1 2 3 4 5 6 7 8 9 10 11
let special: "Ada"; special = "Ada"; // OK special = "Bda"; // Error : Type "Bda" is not assignable to type "Ada". let someString = ""; someString = special; // OK let someString2: ""; someString2 = special; // Error : Type "Ada" is not assignable to type '""';
3.4 엄격한 null 검사
- 엄격한 null검사는 모든 데이터 타입에
null
과undefined
를 할당 받을 시 오류를 출력한다. null
타입이 설정된 변수에undefined
를 할당할 수 없고,undefined
타입이 설정된 변수에null
을 할당받을 수 없다.tsconfig.json
에"strictNullChecks": true
로 설정되어 엄격한null
검사를 사용하는 것인데, 만약null
,undefined
를 항상 서브 타입으로 할당할 수 있게 해주려면false
값으로 변경하면 된다.혹은 애너테이션 또는 유니온 또한 사용할 수 있다.
1 2 3 4 5 6 7 8 9 10 11 12 13
let a: null = null; // OK let b: undefined = undefined; // OK b = null; // Error : Type "null" is not assignable to type "undefined". let d: string = null; // Error : Type "null" is not assignable to type "string". let e: string | null = null; // OK // "strictNullChecks": false let f: string = null; // OK
3.4.1 십억 달러의 실수
null
값을 사용하도록 허용하는 시스템을 말한다.다음 코드에서
strictNullChecks
는 비활성화한다면 코드의 타입이 안전하다고 간주되지만 이는 잘못된 것이다.1 2 3
let nameMaybe = Math.random() > 0.5 ? "Tony" : undefined; nameMaybe.toLowerCase();
- 위 예제는 무작위로 출력되는 숫자가 만약 0.5를 넘는다면,
nameMaybe
는Tony
라는 값을 갖게 되고, 이 외의 경우에는undefined
를 갖게 된다. undefined
에는toLowerCase
메서드를 사용할 수 없지만,strictNullChecks
를 비활성화 했기 때문에 이를 인식하지 못 한다.- 이를 활성화 시켜주거나
if
문으로 활용해 처리해야 한다.
1 2 3 4 5 6 7 8
let nameMaybe = Math.random() > 0.5 ? "Tony" : undefined; nameMaybe.toLowerCase(); // Error : Object is possibly 'undefined'. if (nameMaybe) { nameMaybe.toLowerCase(); // OK }
- 위 예제는 무작위로 출력되는 숫자가 만약 0.5를 넘는다면,
3.4.2 참 검사를 통한 내로잉
- 자바스크립트에서 참 또는 truthy는
&&
연산자 또는if
문처럼true
로 간주된다. false
,0
,-0
,0n
,""
,null
,undefined
,NaN
은falsy
로 정의되어false
로 간주된다.- 논리 연산자는 참 여부를 검사하는 일도 수행하지만, 참 여부 확인 외에 다른 기능은 제공하지 않기 때문에, 만약 값이
falsy
라면 그것이 빈 문자열인지undefined
인지 알 수 없다. - 빈 문자열은
string
타입을 갖고는 있지만falsy
로 정의된다.- 따라서
toLowerCase
등의string
메서드를 사용할 수는 없지만 이것을 인식할 수 없다.
- 따라서
1 2 3 4 5 6 7 8 9 10
let geneticist = Math.random() > 0.5 ? "Barbara" : undefined; if (geneticist) { geneticist.toLowerCase(); // OK: string } geneticist.toUpperCase(); // Error : Object is possibly 'undefined'. geneticist && geneticist.toUpperCase(); // OK: string|undefined geneticist?.toLowerCase(); // OK: string|undefined
- 논리 연산자는 참 여부를 검사하는 일도 수행하지만, 참 여부 확인 외에 다른 기능은 제공하지 않기 때문에, 만약 값이
3.4.3 초깃값이 없는 변수
- 자바스크립트에서 초깃값이 없는 변수는 기본적으로
undefined
를 갖고, 타입스크립트는 값이 할당되기 전에 접근하려고 한다면 오류를 나타낸다. ?.
연산자는 오ㅂ ㅂ셔널 체이닝을 적용하고, 옵셔널 체이닝은 해당 값이 존재하지 않는 경우,undefined
일 때도 오류를 발생시키지 않고undefined
를 반환한다.mathematician
이null
같은 값이 들어있을 경우 오류가 발생할 수 있다.undefined
값이 들어가 있는 변수를 사용할 때는?.
연산자를 사용하여 옵셔널 체이닝을 적용하도록 권장한다.1 2 3 4 5 6
let mathematician: string; mathematician?.length; // Error : Variable 'mathematician' is used before being assigned. mathematician = "Mark"; mathematician.length; // OK: string
3.5 타입 별칭
조금 긴 유니언 타입을 가진 경우, 이를 묶어 쉬운 이름을 할당하는 타입 별칭이 있다.
1 2 3 4 5 6 7 8
let raw: boolean | string | number | null | undefined; let raw1: boolean | string | number | null | undefined; let raw2: boolean | string | number | null | undefined; // 타입 별칭 type RawData = boolean | string | number | null | undefined; let raw: RawData; let raw1: RawData; let raw2: RawData;
3.5.1 타입 별칭은 자바스크립트가 아니다.
- 타입 별칭은 애너테이션처럼 자바스크립트로 컴파일되지 않는다.
- 타입스크립트 타입 시스템에만 존재한다.
타입 별칭은 타입 시스템에만 존재하기 때문에 런타임 코드에서는 참조할 수 없으며, 이에 접근하려고 하면 타입 오류를 나타낸다.
1 2 3 4
// 3.5의 코드의 자바스크립트 컴파일 let raw; let raw1; let raw2;
1 2 3 4
type SomeType = string | undefined; cosole.log(SomeType); // Error : 'SomeType' only refers to a type, but is being used as a value here.
3.5.2 타입 별칭 결합
타입 별칭은 다른 타입 별칭을 참조할 수 있으며, 참조할 타입 별칭을 나중에 선언해도 된다.
1 2 3 4 5 6
type Id = number | string; type IdMaybe = Id | undefined | null; // Id = number|string|undefined|null type Id2 = Id3 | number | string | undefined; type Id3 = boolean;
3.6
- 유니언 타입으로 두 개 이상의 타입 중 하나를 나타낼 수 있다.
- 타입 애너테이션으로 유니언 타입을 명시적으로 표현할 수 있다.
- 타입 내로잉으로 값의 가능한 타입을 좁힐 수 있다.
- 리터럴 타입을 선언할 때는
const
를 사용하여 할당하는 것이 안정적이다.- 원시 타입이 아닌 리터럴 타입을 할당할 떄는 구체적인 값을 할당하는데,
let
키워드를 사용하면 이 값은 언제든지 재할당 될 수 있기 때문에 타입의 안정성을 잃게 된다. - 따라서 리터럴 타입을 사용할 떄, 값을 변경할 필요가 없을 때는
const
를 사용하여 타입의 안정성을 높인다.
- 원시 타입이 아닌 리터럴 타입을 할당할 떄는 구체적인 값을 할당하는데,
- 타입스크립트에서
strictNullChecks
를 활성화하여 엄격한null
검사를 할 수 있다. - 존재하지 않을 수 있는 값이나 할당되지 않은 변수는
undefined
를 갖는다. - 반복적으로 사용하고자하는 타입은, 타입 별칭에 저장하여 사용할 수 있다.
chap.4
4.1 객체 타입
- 객체 리터럴을 생성하면, 값과 동일한 속성명과 원시 타입을 갖게 된다.
값의 속성에 접근하려면
value.key
,value[key]
로 접근을 해야한다.1 2 3 4 5 6 7 8 9
const poet = { born: 1935, name: "Mary", }; poet.born; // number poet[name]; // string poet.end; // Error: Property 'end' does not exist on type '{born: number, name: string}'.
4.1.1 객체 타입 선언
1
2
3
4
5
6
7
8
9
10
11
12
let poet: {
born: number;
name: string;
};
poet = {
born: 1954,
name: "Mary",
};
poet = "sara";
// Error: Type 'string' is not assignable to type '{born: number, name: string}'.
4.1.2 별칭 객체 타입
마찬가지로 별칭 타입을 사용할 수 있다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14
type Poet = { born: number; name: string; }; let poet: Poet; // OK poet = { born: 1935, name: "Mary", }; poet = "sara"; // Error: Type 'string' is not assignable to type '{born: number, name:string}'.
4.2 구조적 타이핑
- 타입스크립트의 타입 시스템은 구조적으로 타입화되어 있다.
- 타입을 충족하는 모든 값을 해당 타입의 값으로 사용할 수 있으며, 매개변수나 변수가 특정 객체 타입으로 선언되면 타입스크립트에 어떤 객체를 사용하든 해당 속성이 있어야 한다.
- 구조적 타이핑은 덕 타이핑과는 다르며, 타입스크립트의 타입 검사기에서 구조적 타이핑은 정적 시스템이 타입을 검사하는 경우이다.
- 덕 타이핑은 런타임에서 사용될 때까지 객체 타입을 검사하지 않는 것을 말한다.
- 자바스크립트는 덕 타입인 반면 타입스크립트는 구조적으로 타입화된다.
덕 타이핑이란 동적 타이핑의 한 종류로 객체의 변수 및 메서드의 집합이 객체의 타입을 결정하는 것을 말한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
type WithFirstName = { firstName: string; }; type WithLastName = { lastName: string; }; const hasBoth = { firstName: "Mary", lastName: "chang", }; let withFirstName: WithFirstName = hasBoth; // OK: 'hasBoth'는 'string'타입의 'firstName'을 포함한다. let withLastName: WithLastName = hasBoth; // OK: 'hasBoth'는 'string'타입의 'lastName'을 포함한다.
4.2.1 사용 검사
- 애너테이션을 사용할 때 애너테이션된 위치에 해당 객체 타입을 할당할 수 있는지 확인한다.
- 할당하는 값에는 객체 타입의 필수 속성이 있어야 한다.
객체 타입에 필요한 객체가 없다면 타입 오류가 발생한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
type firstNames = { first: string, last: string; }; const examle: firstNames = { first: 'Sara', last: 'Nio'; }; // OK const examleple: firstNames = { first: 'Choi', }; // Error: Property 'last' is missing in type '{first: string}' // but required in type 'FirstNames'. type Time = { start: Date; }; const today: Time = { start: "1989-02-03", // Error: Type 'string' is not assignable to type 'Date'. }
초과 속성 검사
객체 타입으로 선언되고, 초깃값에 객체 타입에서 정의된 것보다 많은 필드가 있다면 타입스크립트에서 오류가 발생한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
type Poet = { born: number; name: string; }; // OK const poetMatch: Poet = { born: 1925, name: "Min", }; // OK const poetUnmatch: Poet = { born: 1832, name: "Gone", activity: "Cied", // Error: Type '{activity: string, born: number, name: string}' is not assign to type 'Poet'. // Object literal may only specify known properties, // and 'activity' does not exist in type 'Poet'. }; const poetUnmatch: Poet = poetMatch; // Ok // 초깃값이 Poet와 일치하기 때문에 타입 오류가 발생하지 않는다.
4.2.3 중첩된 객ㅊ 타입
- 객체는 다른 객체의 멤버로 중첩될 수 있고
{...}
객체 타입을 사용한다. 속성의 형태를 객체 타입으로 추출하는 방법도 있다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
type Poem = { author: { first: string; last: string; }; name: string; }; // OK const poemMatch: Poem = { author: { first: "Sara", last: "Man", }, name: "Poe", }; // OK const poemUnmatch: Poem = { author: { name: "Sara", }, // Error: Type '{name: string}' is not assignable // to type '{first: string, last: string}'. // Object literal may only specify known properties, and 'name' // does not exist in type '{first: string, last: string}'. }; type Author = { first: string; last: string; }; type man = { author: Author; name: string; }; const child: man = { author: { name: "Kin", // Error: type '{name: string}' is not assignable to type 'Author'. // Object literal may only specify known properties // and 'name' does not exist in type 'Author'. }, };
4.2.4 선택적 속성
- 모든 객체에 객체 타입 속성이 필요한 것은 아니며, 타입의 속성 애너테이션
:
앞ㅇ?
를 추가하면 선택적 속성임을 나타낼 수 있다. - 선택적 속성과
undefined
를 포함한 유니언 타입 속성 사이에는 차이가 있으며,?
을 사용해 선택적으로 선언된 속성은 존재하지 않아도 된다. 필수로 선언된 속성과
|undefined
는 그 값이undefined
일지라고 반드시 존재해야 한다.1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
type Book = { author?: string; pages: number; }; // OK const ok: Book = { author: "Rina", pages: 80, }; // OK const notOk: Book = { author: "Jin", // Error: Property 'pages' is missing in type // '{pages: number}' but required in type 'Book'. }; type Writer = { author: string | undefined; editor?: string; }; const man = { author: undefined, }; // OK const child = {}; // Error: Property 'author' is missing in type '{}' but required in type 'Writer'.
4.3 객체 타입 유니언
4.3.1 유추된 객체 타입 유니언
- 변수에 여러 객체 타입 중 하나가 될 수 있는 초깃값이 주어지면 해당 타입을 객ㅊ 타입 유니언으로 유추한다.
유니언 타입은 객체 타입을 구성하고 있는 요소를 모두 가질 수 있다.
1 2 3 4 5 6 7 8 9 10
const poem = Math.random() > 0.5 ? { name: "Choi", pages: 7 } : { name: "Kim", rthymes: true }; // name 속성을 항상 가지며, // pages와 rthymes가 있을 수도 없을 수도 있다. poem.name; // string poem.pages; // number|undefined poem.rthymes; // booleans|undefined
4.3.2 명시된 객체 타입 유니언
- 객체 타입의 조합을 명시하면 객체 타입을 더 명확히 정의할 수 있으며, 타입을 더 많이 제어할 수 있다는 이점이 있다.
- 값의 타입이 객체 타입으로 구성된 유니언이라면 모든 유니언 타입에 존재하는 속성에 대한 접근만 허용한다.
필수 속성을 항상 갖는 유니언 타입을 작성하면, 항상 갖는 속성에 접근하는 것은 허용되지만, 존재할 수도, 하지 않을 수도 있는 속성에 접근하는 것은 오류가 발생한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
type Poem = { name: string; pages: number; }; type Person = { name: string; age: number; } type Book = Poem|Person; type ebook : Book = Math.random() > 0.5 ? {nmae: "Choi", pages: 5} : {name: "Kim", age: 25}; ebook.name; //OK ebook.age; // Error: Property 'age' does not exist on type 'Book'. // Property 'age' does not exist on type 'Poem'. ebook.pages; // Error: Property 'pages' does not exist on type 'Book'. // Property 'pages' does not exist on type 'Poem'.
4.3.3 객체 타입 내로잉
타입 검사기가 유니언 타입 값에 특정 속성이 포함된 경우에만 코드 영역을 실행할 수 있음을 알게 되면, 값의 타입을 해당 속성을 포함하는 구성 요소로만 좁힌다.
1 2 3 4 5
if ("pages" in Book) { poem.pages; // OK: poem은 Poem으로 좁혀진다. } else { poem.rhythms; // OK: poem은 Person으로 좁혀진다. }
타입스크립트는
if
와 같은 형식으로 참 여부를 확인하는 것은 허용하지 않으며, 존재하지 않는 객체의 속성에 접근하려고 시도하면 타입 가드처럼 작동하는 방식으로 사용되더라도 오류로 간주된다.1 2 3
if(poem.pages){...} // Error: Property 'pages' does not exist on type 'Poem|Person'. // Property 'pages' does not exist on type 'Person'.
4.3.4 판별된 유니언
- 유니언 타입으로 된 객체는, 객체의 속성이 객체의 형태로 나타내도록 한다.
- 이러한 타입 형태를 판별된 유니언이라고 부르며, 객체의 타입을 가리키는 속성이 판별값이다.
타입스크립트는 코드에서 판별 속성을 사용해 타입 내로잉을 수행한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
type Pages = { name: string; pages: number; type: "pages"; }; type Rhymes = { name: string; rhymes: boolean; type: "rhymes"; }; type Poem = Pages | Rhymes; const poem: Poem = Math.random() > 0.5 ? { name: "Choi", pages: 7, type: "pages" } : { name: "Kim", rhymes: true, type: "rhymes" }; if (poem.type === "pages") { console.log(`It's got pages: ${poem.pages}`); // OK } else { console.log(`It rhymes: ${poem.rhymes}`); } poem.type; // type: "pages" | "rhymes" poem.pages; // Error: Property 'pages' does not exist on type 'Poem'. // Property 'pages' does not exist on type 'Rhymes'.
4.4 교차 타입
- 유니언 타입은 둘 이상의 다른 타입 중 하나의 타입이 될 수 있음을 나타낸다.
타입스크립트에서도 & 교차 타입을 사용해 여러 타입을 동시에 나타낸다.
1 2 3 4 5 6 7 8 9 10 11 12
type Artwork = { genre: string; name: string; }; type Writing = { pages: number; name: string; }; type WrittenArt = Artwork & Writing; // {genre:string; name:string; pages:number;} 와 같다.
교차 타입은 유니언 타입과 결합할 수 있으며, 하나의 타입으로 판별된 유니언 타입을 설명하는데 매우 유용하다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
type ShortPoem = { author: string } & ( | { kigo: string; type: "choi" } | { meter: number; type: "vill" } ); const moringGlory: ShortPoem = { author: "Fukuda", kigo: "Morning Glory", type: "choi", }; // OK const oneArt: ShortPoem = { author: "Elizabeth", type: "vill", // Error: Type '{author: string; type: "vill"}' // is not assignable to type 'ShortPoem' // Type '{author: string; type: "vill"}' is not assignable to // type '{author:string;}' & {metter: number; type: "vill"}' // Property 'meter' is missing in type '{author: string; type:"vill"}' // but required in type '{meter:number; type:"vill";}'. };
4.4.1 교차 타입의 위험성
긴 할당 가능성 오류
- 유니언 타입과 결합하는 것처럼 복잡한 교차 타입을 만들게 되면 할당 가능성 오류 메세지를 읽기 어려워진다.
- 타입을 일련의 별칭으로 된 객체 타입으로 분할하면 읽기 쉬워진다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
type ShortPoemBase = { author: string }; type Haiku = ShortPoemBase & { kigo: string; type: "haiku" }; type Vill = ShortPoemBase & { meter: number; type: "vill" }; type ShortPoem = Haiku | Vill; const oneArt: ShortPoem = { author: "Elizabeth", type: "vill", // Error: Type '{author: string; type: "vill";}' // is not assignable to type 'ShortPoem'. // Type '{author: string; type: "vill";}' // is not assignable to type 'Vill'. // Property 'meter' is missing in type '{author: string; type: "vill";}'. // but required in type '{meter: number; type: "vill";}'. };
never
- 교차 타입은 잘못 사용하기 쉽고 불가능한 타입을 생성한다.
- 원시 타입의 값은 동시에 여러 타입이 될 수 없기 때문에 교차 타입의 구성요소로 함께 결합할 수 없다.
- 두 개의 원시 타입을 함께 시도하면 never 키워드로 표시되는 never타입의 된다.
1 2 3 4 5 6 7
type NotPossible = number & string; // 타입: never let notNumber: NotPossible = 0; // Error: Type 'number' is not possible to type 'never'. let notString: never = ""; // Error: Type 'string' is not possible to type 'never'.
4.5
- 타입스크립트가 객체 타입 리터럴을 해석하는 방법
- 중첩과 선택적 속성을 포함한 객체 리터럴 타입 소개
- 객체 리터럴 타입의 유니언 타입 선언, 추론 및 타입 내로잉
- 판별된 유니언 타입과 판별값
- 교차 타입으로 객체 타입을 결합하는 방법