바닐라자바스크립트
* 코드의 문서화
interface Painter {
// Painter에 세가지 속성이 있다, 그중 2가지는 메서드이다.
finish(): boolean;
ownMaterials: Material[];
paint(
painting: string,
materials: Material[]
): boolean;
}
function paintPainting(
painter: Painter,
painting: string
): boolean{ (...) }
많은 자바스크립트 프로젝트는 소스코드를 실행할 수 있는 자바스크립트로 변환하기 위해서 타입스크립트의 자체 컴파일러 대신 '바벨'같은 전용변환기를 사용한다.
tsconfig.json 파일을 생성할 때의 또다른 이점은 편집기에서 특정 폴더를 열었을 때 편집기가 해당폴더를 타입스크립트 프로젝트로 인식한다는 것, vscode에서 폴더를 열면 타입스크립트 코ㅡ를 분석하는데 사용하는 설정은 해당 폴더의 tsconfig.json을 따르게 된다.
vscode는 타입스크립트를 지원하고 자체적으로 타입스크립트로 빌드된다.
타입스크립트의 제약
타입시스템
오류종류
* 할당 가능성
let firstName = "Carole";
firstName = 'Joan';
할당가능성 : 타입스크립트에서 함수호출이나 변수에 값을 제공할 수 있는지 여부를 확인하는 것. 즉, 전달된 값이 예상된 타입으로 할당 가능한지의 여부를 확인한다.
'Type ...is not assignable to type...' 형태의 가장 일반적인 오류
- 해당 오류메시지에 언급된 첫번째 typt은 : 코드에서 변수에 할당하려고 시도하는 값이다.
- 두번째 type은 : 첫번째 타입. 즉, 값이 할당되는 변수이다.
- 변수에 타입스크립트가 읽어야 할 초기값이 없는 경우도 있다. tsc는 나중에 사용할 변수의 초기 타입을 파악하려고 시도하지 않난다. 그리고 기본적으로 변수를 암묵적인 any 타입으로 간주한다. 즉, 변수는 세상의 모든 것이 될 수 있음을 나타냄.
- 초기타입을 유추할 수 없는 변수는 `진화하는 any` 라고 부름. 특정 타입을 강제하는 대신 새로운 값이 할당 될 때마다 변수타입에 대한 이해를 발전시킨다.
// 타입: any
let rocker;
// 타입: string
rocker = "John Jett"
// Ok
rocker.toUpperCase();
// 타입: number
rocker = 19.58;
// Ok
rocker.toPrecision(1);
// Error: 'toUpperCase'does not exist on type 'number'.
rocker.toUpperCase();
tsc는 초기값을 할당하지 않고도 변수의 타입을 선언할 수 있는 구문인 타입애너테이션typeAnnotation을 제공한다. 이것은 변수 이름의 뒤에 배치되며 콜론(:)과 타입 이름을 차례대로 기재함.
변수에 타입애너테이션으로 정의한 타입 외의 값을 할당하면 타입 오류발생한다.
* 불필요한 타입애너테이션
- tsc가 자체적으로 수집할 수 없는 정보를 타입스크립트에 제공할 수 이싸. 타입을 즉시 유추할 수 있는ㄴ 변수에도 애너테이션을 사용할 수 있다.
let firstName: string = "Tina";
- 아무것도 변하지않는 변수에는 타임에너ㅔ이션을 ㅊ가하지않는것을 선호한다.
- 코드를 명확하게 문서화하거나 실수로 변수타입이 변경되지 않도록 타입스크립트를 보호하기 위해 변수에 명시적으로 타입애너테이션을 포함하는 것이 경우에 따라서는 유용할 수 있다.
- 타입스크립트는 변수에 할당 된 값이 원래 타입과 일치하는지 확인하는 것 이상을 수행
import {value} from './values';
export const doubled = value * 2;
scope : 변수에 접근할 수 있는 범위
모듈 : export 또는 import가 있는 파일
스크립트 : 모듈이 아닌 모든 파일
- 파일이 스크립트면 타입스크립트는 해당 파일을 전역 스코프로 간주하므로 모든 스크립트가 파일의 내용에 접근가능. 즉, 스크립트 파일에 선언 된 변수는 다른 스크립트 파일에 선언된 변수와 동일한 이름을 가질 수 없다.
- 타입스크립트 파일에 Cannot redeclare... 라는 오류 : 파일에 아직 export 또는 import 문을 추가하지 않았기 때문일 수 있다. ECMA스크립트 사양에 따라 export 또는 import 문 없이 파일을 모듈로 만들어야 한다면 파일 아무곳에나 export{};를 추가해서 강제로 모듈이 되도록 만들며 ㄴ됨.
상수를 제외한 모든 것은 변한다.
시간이 지나면서 값도 변할 수 있다.
1. 유니언타입
let mathematician = Math.random() > 0.5 ? undefined : "Mark Goldberg";
- mathematician은 어떤 타입??
- 둘 다 잠재적 타입이긴 하지만 무조건 undefined거나 혹은 무조건 string도 아니다. mathematician은 undefined 아거나 string 일 수 있다. '이거 혹은 저거' 와 같은 타입을 말한다.
let mathmatician: string | undefined
let thinker: string | null = null;
if (Math.random() > 0.5) {
thinker = "Susanne Langer";
}
- 유니언타입 선언의 순서는 중요하지 않다.
- 모든 유니언 타입에 존쟇지 안ㅇㅎ는 속성에 대한 접근을 제한하는 것은 안전조치에 해당한다.
- 유니언타입으로 정의된 여러 타입 중 하나의 타입으로 된 값의 속성을 사용하려면 코드에서 값이 보다 구체적인 타입 중 하나라는 것을 tsc에 알려야 한다. 이러한 과정을 : 내로잉
2. 내로잉
- 값이 정의,선언 혹은 이전에 유추 된 것보다 더 구체적인 타입임을 코드에서 유추하는 것이다.
- tsc가 값의 타입이 이전에 알려진 거솝다 더 좁혀졌다는 것을 알게 되면 값을 더 구체적인 타입으로 취급한다. 타입을 좁히는데 사용할 수 있는 노리적 검사를 `타입가드` 라고 한다.
- typeof 연산자를 사용한 내로잉
let researcher = Math.random() > 0.5 ? "Rosalind Frankie" : 51;
if (typeof researcher === 'string') {
researcher.toUpperCase();
}
* 리터럴 타입
- philosopher 변수를 보자
const philosopher = "Hypatia";
- philosopher 의 타입은 ?
String타입이다.
philosopher 는 원시타입 값 중 어떤 것이 아닌 특정 원시값으로 알려진 타입. 이것이 리터럴 타입
- 원시타입 string 은 존재할 수 있는 모든 가능한 문자열의 집합을 나타내지만, 리터럴 타입인 "Hypatia"는 하나의 문자열만 나타냄
- 변수를 const 로 선언하고 직접 리터럴 값을 할당하면 tsc는 해당 변수를 할당 된 리터럴 값으로 유추한다.
- 각 원시타입은 해당 타입이 가질 수 있는 가능한 모든 리터럴 값의 전체 조합으로 생각할 수 있다. 즉, 원시타입은 해당 타입의 가능한 모든 리터럴 값의 집합이다.
const lifespan: number | "ongoing" | "uncertain";
lifespan = 89;
lifespan = "ongoing";
lifespan = true;
// Error : Type 'true' is not assignable to type ' number|"ongoing"|"uncertain" '
* 리터럴 할당 가능성
const specificallyAda: "Ada";
specificallyAda = "Ada";
// Error: Type '"Byron"' is not assignable to type '"Ada"'.
specificallyAda = "Byron";
// 타입: string
const someString = "";
// Error : Type 'string' is not assignable to type 'Ada'
specificallyAda = someString;
0과 1처럼 동일한 원시타입이어도 서로 다른 리터럴 타입은 서로 할당 할 수 없다.
specificallyAda 는 리터럴타입 Ada 로 선언했으므로 값에 Ada를 할당할 수 있지만,
Byron 이나 string 타입 값은 할당할 수 없다.
그러나 리터럴 타입은 그 값이 해당하는 원시 타입에 할당할 수 있다.
* null검사
- '잠재적으로 정의되지않은 undefined 값'
- 다른 타입이 필요한 위치에서 null값을 사용하도록 허용하는 많은 타입시스템을 가리키는 용어
- 엄격한 null검사를 활성화해야만 코드가 null 또는 undefined 값으로 인한 오류로부터 안전한지 여부를 쉽게 파악할 수 있다.
* 참 검사를 통한 내로잉
- jsc에서 참 또는 truthy(boolean문맥에서 참으로 평가되는것)는 && 연산자 또는 if 문처럼 boolean 문맥에서 true로 간주된다.
- jsc에서 false, 0, -0, 0n, "", null, undefined, NaN 처럼 falsy 로 정의된 값을 제외한 모든 값은 참이다.
- tsc는 잠재적인 값 중 truthy로 확인 된 일부에 한해서만 변수의 타입을 좁힐 수 있다.
let geneticist = Math.random() > 0.5 ? "Barbara McClintock" : undefined;
- geneticist 는 string|undefined 타입이며, undefined 는 항상 falsy 이므로 tsc는 if 문의 코드블록에서는 geneticist 가 string 타입이 되어야 한다고 추론가능.
* 초기값이 없는 변수
- jsc에서 초기값이 업ㅇㅅ는 변수는 기본적으로 undefined 가 된다. 만약 undefined를 포함하지 않는 타입으로 변수를 선언한 다음, 값을 할당하기 전에 사용하려고 시도한다면 다음과 같은 오류메시지가 나타난다.
const mathematician: string;
mathematician?.length;
//Error: Variable 'mathematician' is used before being assigned
* 타입스크립트에는 재사용하는 타입에 더 쉬운 이름을 할당하는 타입별칭이 있다.
* 타입별칭은 : type 새로운이름 = 타입 의 형태를 갖는다.
type MyName = ...;
타입 별칭은 자바스크립트가 아니다. 런타임 코드에서는 참조할 수 없다. 타입스크립트는 런타임에 존재하지 않는 항목에 접근하려고 하면 타입 오류로 알려준다.
type SomeType = string | undefined;
console.log(SomeType);
//Error: 'SomeType' only refers to a type, but is being used a value here.
* 타입별칭은 다른 타입 별칭 참조가 가능하다.
* 유니언 타입인 타입 별칭 내에 또다른 유니언 타입인 타입 별칭을 포함하고 있다면 다른 타입 별칭을 참조하는 것이 좋다.
type Id = number | string;
type IdMaybe = Id | undefined | null;
// IdMaybe 타입은 다음과 같음. number | string | undefined | null
{...}구문을 사용해서 객체 리터럴을 생성하면 타입스크립트는 해당 속성을 기반으로 새로운 객체 타입 또는 타입형태를 고려한다.
해당 객체타입은 객체의 값과 동일한 속성명과 원시 타입을 갖는다.
값의 속성에 접근하려면 value.멤버
또는 value['멤버']
구문을 사용한다.
다음 poet 변수의 타입은 number 타입인 born 과 string 타입인 name 으로 이루어진 두개의 속성을 갖는 객체이다. 이 두개의 속성에 접근하는 것은 허용되지만, 다른 속성 이름으로 접근하려고 하면 해당 이름이 존재하지 않는다는 타입오류가 발생한다.
const poet = {
born: 1935,
name: "Mary Oliver"
};
poet['born'];
poet.name;
poet.end;
// Error: Property 'end' does not exist on type '{born: number; name: string;}'
- 객체타입은 타입스크립트가 자바스크립트 코드를 이해하는 방법에 대한 핵심개념이다.
- null 과 undefined 를 제외한 모든 값은 그 값에 대한 실제타입의 멤버집합을 가지므로 타입스크립트는 모든 값의 타입을 확인하기 위해 객체타입을 이해해야한다.
* 객체타입 선언
- 기존 객체에서 직접 타입을 유추하는 것도 좋지만, 결국에는 객체의 타입을 명시적으로 선언하는게 좋다.
- 명시적으로 타입이 선언된 객체와는 별도로 객체의 형태를 설명하는 방법필요.
- 객체타입은 객체리터럴과 유사하게 보이지만 필드값 대신 타입을 사용해 설명한다.
const poetLater: {
born: number;
name: string;
};
poetLater = {
born: 1935,
name: "Mary Oliver";
}
poetLater = "Sappho";
// Error: Type 'string' is not assignable to type '{born: number; name:string;}'
* 별칭객체 타입
{...} → 이렇게 객체타입을 계속 작성하지 말고 각 객체 타입에 타입 별칭을 할당해 사용하자, 타입스크립트의 할당 가능성 오류 메시지를 좀더 직접적으로 읽기 쉽게 만드는 추가이점있음.
type Poet = {
born: number;
name: string;
};
const poetLater: Poet;
poetLater = {
born: 1935,
name: "sara Teasdale"
};
poetLater = "Emily Dickinson";
// Error: Type 'string' is not assignable to "Poet'
- 대부분의 타입스크립트 프로젝트는 객체 타입을 설명할 때 인터페이스 키워드를 사용하는 것을 선호한다. 별칭 객체타입과 인터페이스는 거의 동일하다.
- 타입스크립트가 객체 리터럴을 해석하는 방법을 이해하자
구조적 타이핑
- 타입스크립트의 타입시스템은 구조적으로 타입화 되어있다. 즉, 타입을 충족하는 모든 값을 해당 타입으 ㅣ값으로 사용할 수 있다. 매개변수나 변수가 특정 객체 타입으로 선언되면 타입스크립트에 어떤 객체를 사용하든 해당 속성이 있어야 한다고 말해야한다.
type WithFirstName = {
firstName: string;
};
type WithLastName = {
lastName: string;
};
const hasBoth = {
firstName: "Lucille",
lastName: "Clifton"
};
const withFirstName: WithFirstName = hasBoth;
const withLastName: WithLastName = hasBoth;
- 별칭객체타입인 WithFirstName 과 WithLastName 은 오직 string 타입의 단일멤버만 선언한다.
- hasBoth 변수는 명시적으로 선언되지 않았음에도 두개의 별칭 객체타입을 모두 가지므로 두개의 별칭 객체 타입 내에 선언된 변수를 모두 제공할 수 있다.
- 덕타이핑 : 동적 타이핑의 한 종류로 객체의 변수 및 메서드의 집합이 객체의 타입을 결정하는 것을 의미한다.
사용검사
type FirstAndLastNames = {
first: string;
last: string;
};
consst hasBoth: FirstAndLastNames = {
first: "Sarojini",
last: "Naidu"
};
const hasOnlyOne: FirstAndLastNames = {
// 두가지 속성이 모두 없는 객체는 사용할 수 없다
//Error: Property 'last' is missing in type '{first: string}'
// but required in type 'FirstAndLastNames'.
first: "Sappho"
}
- 별칭객체타입인 FirstAndLastNames 는 first와 last 속성이 모두 있어야 한다.
- 두가지 속성을 모두 포함한 객체는 FirstAndLastNames 타입으로 선언된 변숭 ㅔ사용할 수 있지만 두가지 속성이 모두 없는 객체는 사용할 수 없다.
- 둘 사이에 일치하지 않는 타입도 허용되지 않는다. 객체 타입은 필수속성 이름과 해당 속성이 예상되는 타입을 모두 지정한다. 객체의 속성이 일치하지 않으면 타입스크립트는 타입 오류를 발생시킨다.
type TimeRange = {
start: Date;
};
const hasStartString: TimeRange = {
start: "1879-02-13";
//Error: Type 'string' is not assignable to type 'Date'.
}
- TimeRange타입은 start속성을 Date타입으로 예상한다. 하지만, hasStartString객체의 start속성이 Date가 아니라 string타입이므로 타입오류 발생.
초과속성검사
//1.
type Poet = {
born: number;
name: string;
};
const poetMatch: Poet = {
born: 1928,
name: "Maya Angelou"
};
const extraProperty: Poet = {
activity: "walking",
//Error: Type '{activity: string; born: number; name: string;}'
// is not assignable to type 'Poet'.
// Object literal may only specify known properties,
// and 'activity' does not exist in type 'Poet'.
born: 1935,
name: "Mary Oliver"
};
- poetMatch변수는 별칭객체타입에 정의 된 필드가 Poet에 정확히 있지만, 초과 속성이 있는 extraProperty는 타입오류 발생시킴.
- 초과 속성검사는 객체타입으로 선언된 위치에서 생성되는 객체 리터럴에 대해서만 일어난다. 기존 객체 리터럴을 제공하면 초과 속성 검사를 우회한다.
//2.
const existingObject = {
activity: "walking",
born: 1935,
name: "Mary Oliver"
};
const extraPropertyButOk: Poet = existingObjext;
- extraPropertyButOk변수는 초기값이 구조적으로 Poet와 일치하기 때문에 타입오류가 발생하지 않음.
중첩된 객체타입
//1.
type Poem = {
author: {
firstName: string;
lastName: string;
};
name: string;
};
const poemMatch: Poem = {
author: {
firstName: "Sylvia",
lastName: "Plath"
},
name: "Lady Lazarus"
};
const poemMismatch: Poem = {
author: {
name: "Sylvia Plath"
// Error: Type '{name: string;}' is not assignable
// to type '{firstName: string; lastName: string;}'
// Object literal may only specify known properties, and 'name'
// does not exist in type '{firstName: string; lastName: string;}'
},
name: "Tulips"
};
- Poem타입은 author 속성이 firstName:string 과 lastName:string인 객체로 선언되었다.
- poemMatch변수는 구조가 Poem과 일치하기 떄문에 Poem을 할당할 수 있지만,
- poemMismatch는 author 속성에 firstName과 lastName 대신 name을 포함하니까 할당할 수 없음.
//2.
type Author = {
firstName: string;
lastName: string;
};
type Poem = {
author: Author;
name: string;
};
const poemMismatch: Poem = {
author: {
name: "Sylvia Plath"
// 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'.
},
name: "Tulips"
};
- 이렇게, 중첩된 객체타입을 고유한 타입이름으로 바꿔서 사용하면 코드와 오류메시지가 더 읽기 쉬워짐.
선택적속성
//1.
type Book = {
author?: string;
pages: number;
};
const ok: Book = {
author: "Rita Dove",
pages: 80,
};
const missing: Book = {
// Error: Property 'pages' is missing in type
// '{pages: number;}' but required in type 'Book'.
author: "Rita Dove"
}
- `선택적속성과 undefined를 포함함 유니언타입의 속성 사이에는 차이가 있다!!`
- ?를 사용해 선택적으로 선언된 속성은 존재하지않아도 된다.
- 필수로 선언된 속성과 | undefined 는 그 값이 undefined일지라도 반드시 존재해야함.
//2.
type Writers = {
author: string | undefined;
editor?: string;
};
const hasRequired: Writers = {
author: undefined
};
const missingRequired: Writers = {};
//Error: Property 'author' is missing in type '{}' but required in type 'Writers';
- Writers 타입의 editor 속성은 ? 를 사용해서 선언했으므로 변수를 선언할 때 생략이 가능하다.
- author속성은 ? 가 없으므로 값이 undefined여도 반드시 존재해야한다.
유추 된 객체 타입 유니언
//1.
const poem = Math.random() > 0.5 ? {
name: "The Double Image", pages: 7
} : {
name: "Her kind", rhymes: true
};
// 타입:
// {
// name: string;
// pages: number;
// rhymes?: undefined;
// } | {
// name: string;
// pages?: undefined;
// rhymes: boolean;
// }
poem.name; //string;
poem.pages; //number | undefined
poem.rhymes; //booleans | undefined
명시 된 객체타입 유니언
//1.
type PoemWithPages = {
naem: string;
pages: number;
};
type PoemWithRhymes = {
name: string;
rhymes: boolean;
};
type Poem = PoemWithPages | PoemWithRhymes;
const poem: Poem = Math.random() > 0.5
? {name: "The Double Image", pages: 7}
: {name: "Her Kind", rhymes: true};
poem.name;
poem.pages;
//Error: Property 'pages' does not exist on type 'Poem'.
// Property 'rhymes' does not exist on type 'PoemWithRhymes'.
poem.rhymes;
//Error: Property 'rhymes' does not exist on type 'Poem'.
// Property 'rhymes' does not exist on type 'PoemWithPages'.
- 잠재적으로 존재하지 않는 객체의 멤버에 대한 접근을 제한하면 코드의 안전을 지킬 수 있다.
- 값이 여러타입 중 하나인 경우, 모든 타입에 존재하지 않는 속성이 객체에 존재할거라 보장할 수 없다.
객체타입내로잉
//1.
if ("pages" in poem) {
poem.pages;
} else {
poem.rhymes;
}
- poem의 pages가 타입스크립트의 타입가드 역할을 해서 PoemWithPages임을 나타내는지 확인한다.
- 만일, Poem이 PoemWithPages가 아니라면 PoemWithRhymes이어야 함.
- 타입스크립트는 if(poem.pages)와 같은 형식으로 참 여부를 확ㅇ니하는 것을 허용하지 않음.
- 존재하지 않는 객체의 속성에 접근하려고 시도하면 타입 가드처럼 작동하는 방식으로 사용되더라도 타입오류로 간주됩니다.
판별된 유니언
//1.
type PoemWithPages = {
name: string;
pages: number;
type: 'pages';
};
type PoemWithRhymes = {
name: string;
rhymes: boolean;
type: 'rhymes';
};
type Poem = PremWithPages | PoemWithRhymes;
const poem: Poem = Math.random() > 0.5
? {name: "the double Image", pages: 7, type: "pages"}
: {name: "Her kind", rhymes: true, type: "rhymes"};
if (poem.type === "pages") {
console.log('It's got pages: ${poem.pages}');
} else {
console.log('It rhymes: ${poem.rhymes}');
}
poem.type;
poem.pages;
- Poem타입은 PoemWithPages타입 또는 PoemWithRhymes타입 둘 다 될 수 있는 객체를 설명하고,
- type 속성으로 어느 타입인지를 나타낸다.
- 만일 poem.type이 pages이면, 타입스_는 poem을 PoemWithPages로 유추한다.
- 타입내로잉 없이는 값에 존재하는 속성을 보장할 수 없다.
교차타입
& 교차타입 intersection type
을 사용해 여러타입을 동시에 나타낸다. //1.
type Artwork = {
genre: string;
name: string;
};
type Writing = {
pages: number;
name: string;
};
type WrittenArt = Artwork & Writing;
// 다음과 같음:
// {
// genre: string;
// name: string;
// pages: number;
// }
- Artwork 와 Writing 타입은 genre, name, pages 속성을 결합한 WrittenArt 타입을 형성하는데 사용됨.
- 교차타입은 유니언타입과 결합할 수 있으며, 이는 하나의 타입으로 판별된 유니언타입을 설명하는데 유용.
//2.
type ShortPoem = {author: string} & (
| {kigo: string; type: "haiku";}
| {meter: number; type: "villanelle";}
);
const morningGlory: ShortPoem = {
author: "Fukuda Chiyo-ni",
kigo: "Morning Glory",
type: "haiku"
};
const oneArt: ShortPoem = {
//Error: Type '{author: string, type: "villanelle"}'
// is not assignable to type 'ShortPoem'.
// Type '{author: string, type: "villanelle"}' is not assignable to
// type '{author: string;} & {meter: number; type: "villanelle"}'
// Property 'meter' is missing in type '{author: string, type: "villanelle"}'
// but required in type '{meter: string, type: "villanelle"}'
author: "Elizabeth Bishop",
type: "villanelle"
};
- ShortPoem 타입은 항상 author 속성을 가지며 하나의 type 속성으로 판별된 유니언타입이다.
긴 할당 가능성 오류
//1.
type ShortPoemBase = {author: string;};
type Haiku = ShortPoemBase & {kigo: string; type: 'haiku'};
type Villanelle = ShortPoemBase & {meter: number; type: 'villanelle'};
type ShortPoem = Haiku | Villanelle;
const onArt: ShortPoem = {
//Error: Type '{author: string, type: "villanelle"}'
// is not assignable to type 'ShortPoem'.
// Type '{author: string, type: "villanelle"}' is not assignable to type 'Villanelle'.
// Property 'meter' is missing in type '{author: string, type: "villanelle"}'
// but required in type '{meter: number, type: "villanelle"}'
author: "Elizabeth Bishop",
type: "villanelle"
};
never
type NotPossible = number & string;
- never키워드와 never타입은 프로그래밍 언어에서 bottom타입 또는 empty타입을 뜻한다.
- bottom타입은 값을 가질 수 없고 참조할 수 없는 타입이므로 bottom 타입에 그 어떠한 타입도 제공못함.
const notNumber: NotPossible = 0;
// Error: Type 'number' is not assingnable to type 'never'.
const notString: never = '';
// Error: Type 'string' is not assignable to type 'never'.
- 대부분의 타입스크립트 프로젝트는 never타입을 거의 사용하지 않지만 코드에서 불가능한 상태를 나타내기 위해 가끔 등장.
- 하지만 대부분 교차타입을 잘못 사용해 발생한 실수일 가능성이 높다.