본 글은 Typescript Programming을 요약한 글입니다.
자세한 내요은 본 책을 읽으시길 바랍니다.
- 타입 : 값과 이 값으로 할 수 있는 일의 집합
어떤 값이 T 타입이라면, 이 값을 가지고 어떤 일을 할 수 있고 어떤 일을 할 수 없는지도 알 수 있다. 여기서 중요한 점은 타입검사기를 이용해 유요하지 않은 동작이 실행되는 일을 예방하는 것이다.
3.1 타입을 이야기하다
fuction squareOf(n : number){
return n*n
}
squareOf(2) // 4
squareOf('z') // 에러 TS2345: "z" 라는 타입의 인수는 'number'타입의 매개변수에 할당할 수 없음
이 예제 코드에서 다음을 알 수 있었다.
- squareOf의 매개변수 n은 number로 제한된다.
- 2 값은 number에 할당할 수 있는 타입이다.
일단 타입을 제한하면 타입스크립트가 함수를 호출할 때 호환이 되는 인수로 호출했는지 판단한다. 이를 경계의 개념으로 해석할 수도 있다. 타입스크립트에 n의 상위 한정값이 number라고 알려주면 squareOf에 전달하는 모든 값이 number 이하여야 한다. 만약 number 이상의 것(number 또는 문자열이 될 수도 있는 값)이라면 n에 할당할 수 없게 된다.
3.2 타입의 가나다
타입스크립트가 지원하는 각각의 타입을 살펴보면서 각 타입이 무엇을 포함할 수 있는지, 어떤 동작을 수행할 수 있는 지 알아보자. 타입 별칭, 유니온 타입, 인터섹션 타입 등 여러 가지 언어 기능도 확인한다.
3.2.1 any
타입스크립트에서는 컴파일 타임에 모두가 타입이 있어야 하므로 프로그래머와 타입스크립트 둘 다 타입을 알 수 없는 상황에서는 기본 타입인 any라고 가정한다. any는 모든 값의 집합이므로 any는 모든 것을 할 수 있다. 되도록이면 any를 피하고 반드시 최후의 수단으로만 사용하자.
- TSC 플래그 : nolmplitcitAny
타입스크립트의 기본 설정은 자유를 허용하므로 any로 추론되는 값을 발견하더라도 예외를 발생시키지 않는다. 그러니 암묵적인 any가 나타났을 때 예외를 일으키고 싶다면 tsconfig.json 파일에서 noImplicitAny 플래그를 활성화하자. 그러나 이는 strict 패밀리에 속하기에 strict를 활성화했다면 따로 설정하지 않아도 된다.
3.2.2 unknown
타입을 미리 알 수 없는 어떤 값이 있을 떄 any 대신 unknown을 사용하자. any처럼 unknown도 모든 값을 대표하지만, unknown의 타입을 검사해 정제하기 전까지는 타입스크립트가 unknown 타입의 값을 사용할 수 없게 강제한다. 비교연산(==, ===, ||, &&, ?)과 반전(!)을 지원하고 자바스크립트의 typeof, instanceof 연산자로 정제할 수 있다.
let a : unknown = 30 // unknown
let b : a === 123
let c = a + 10 // 에러 TSC2571 : 객체의 타입이 'unknown'임
if(typeof a === 'number'){
let d = a + 10 // number
}
- 타입스크립트가 무언가의 타입을 unknown이라고 추론하는 상황은 없다. 명시적으로 설정해야 한다.
- unknown 타입이 아닌 값과 unknown 타입인 값을 비교할 수 있다.
- 하지만 unknown 값이 특정 값이라고 가정하고 해당 타입에서 지원하느 동작을 수행할 수 없다. 먼저 해당값이 특정 타입임을 증명해야한다.
3.2.3 boolean
boolean 타입은 true, false 두 개의 값을 갖는다. 비교 연산과 반전 연산만을 사용가능하다.
let a = true // boolean
var b = false // boolean
const c = true // true
let d : boolean = true // boolean
let e : true = true // true
let f : true = false // 에러 TS2322: 'false' 타입을 'true' 타입에 할당할 수 없음
- 어떤 값이 boolean인지 타입스크립트가 추론하게 한다.
- 어떤 값이 특정 boolean인지 타입스크립트가 추론하게 한다.
- 값이 boolean임을 명시적으로 타입스크립트에 알린다.
- 값이 특정 boolean임을 명시적으로 타입스크립트에 알린다.
실제 프로그래밍에서는 보통 첫 번째와 두 번째 방법을 사용한다. 두번째와 네번째 방법은 값을 타입으로 사용하므로 e와 f에 사용할 수 있는 값은 boolean 타입이 가질 수 있는 값 중 특정한 하나의 값으로 한장된다. 이 기능을 타입 리터럴이라 부른다.
- 타입 리터럴 : 오직 하나의 값을 나타내는 타입
const를 사용함으로 타입스크립트는 그 변수의 값이 절대 변하지 않으리라는 사실을 알게 되어 해당 변수가 가질 수 있는 가장 좁은 타입으로 추론한다. 타입 리터럴은 모든 곳에서 일어날 수 있는 실수를 방지해 안전성을 추가로 확보해주는 강력한 언어 기능이다.
3.2.4 number
number 타입은 모든 숫자의 집합이다. 덧셈, 뺄셈, 모듈로(%), 비교 등의 숫자 관련 연산을 수행할 수 있다.
let a = 1234 // number
var b = Infinity * 0.10 // number
const c = 5678 // 5678
let d = a < b // boolean
let e : number = 100 // number
let f : 26.218 = 26.218 // 26.218
let g : 26.218 = 10 // 에러 TS2322 : '10' 타입을 '26.218' 타입에 할당할 수 없음
- 어떤 값이 number인지 타입스크립트가 추론하게 한다.
- const를 이용해 타입스크립트 값이 특정 number임을 추론하게 한다.
- 값이 number임을 명시적으로 타입스크립트에 알린다.
- 값이 특정 number임을 명시적으로 타입스크립트에 알린다.
boolean 방식처럼 개발자들은 대개 타입스크립트가 number 타입을 추론하도록 만든다. number 타입임을 명시해야 하는 상황은 거의 없다.
3.2.5 bigint
새로 추가된 타입으로 라운딩 관련 에러 걱정 없이 큰 정수를 처리할 수 있다. number는 253까지의 정수를 표현하지만 bigint를 이용하면 이보다 큰 수도 표현할 수 있다. 덧셈, 뺄셈, 곱셈, 나눗셈, 비교 등의 연산을 지원한다.
let a = 1234n // bigint
const b = 5678n // 5678n
var c = a + b // bigint
let d = a < 1235 // boolean
let e = 88.5n // 에러 TS1353 : bigint 리터럴은 반드시 정수여야 함
let f : bigint = 100n // bigint
let g : 100n = 100n // 100n
let h : bigint = 100 // 에러 TS2322 : '100'타입은 'bigint'타입에 할당 할 수 없음
boolean과 number처럼 bigint타입도 네가지 방법으로 선언할 수 있으며 가능하다면 타입스크립트가 bigint 타입으로 추론하도록 두는 것이 좋다
3.2.6 string
string은 모든 문자열의 집합으로 연결(+), 슬라이스(.slice) 등의 연산을 수행할 수 있다.
let a = 'hello' // string
var b = 'billy' // string
const c = '!' // '!'
let d = a + '' + b + c // string
let e : string = 'zoom' // string
let f : 'john' = 'john' // 'john'
let g : 'john' = 'zoe' 에러 TS2322 : "zoe" 타입을 "john"타입에 할당할 수 없음
boolean과 number처럼 string타입도 네가지 방법으로 선언할 수 있으며 가능하다면 타입스크립트가 string타입으로 추론하도록 두는 것이 좋다
3.2.7 symbol
symbol은 ES2015에 새로 추가된 기능이다. 실무에서는 자주 사용하지 않는 편이며 객체와 맵에서 문자열 키를 대신하는 용도로 사용한다. 사람들이 잘 알려진 키만 사용하도록 강제할 수 있으므로 키를 잘못 설정하는 실수를 방지한다. 객체의 기본 반복자를 설정하거나 객체가 어떤 인스턴스인지를 런타임 오버라이딩 하는 것과 비슷한 기능을 제공한다. symbol타입으로 할 수 있는 동작은 그렇게 많지 않다.
let a = Symbol('a') //symbol
let b : symbol = Symbol('b') // symbol
var c = a === b //boolean
let d = a + 'X' // 에러 TS2469" '+' 연산을 'symbol' 타입에 적용할 수 없음
만들어진 symbol은 고유하여 다른 symbol과 == 또는 ===로 비교했을 때 같지 않다고 판단된다. symbol도 symbol 타입으로 추론되거나 아니면 멍시적으로 unique symbol을 정의할 수 있다.
const e = Symbol('e') // typeof e
const f : unique symbol = Symbol('f') // typeof f
let g : unique symbol = Symbol('f') // 에러 TS1332 : 'unique symbol' 타입은 반드시 const여야 함
let g = e === e // boolean
let i = e ===f // 에러 TS2367 : 'unique symbol'타입은 서로 겹치는 일이 없으므로 이 비교문의 결과는 항상 'false'
unique symbol도 결국 1, true, 'literal' 등 다른 리터럴 타입과 마찬가지로 특정 symbol를 나타내는 타입이다.
3.2.8 객체
객체 타입은 객체의 형태를 정의한다. 객체 타입({ })만으로 만든 간단한 객체와 복잡한 객체(new로 만든)를 구분할 수 없다. 이는 자바스크립트가 구조 기반 타입을 갖도록 설계되었기 때문이다. 따라서 타입스크립트도 이름 기반 타입 보다 이를 선호한다.
- 구조 기반 타입화 : 객체의 이름에 상관없이 객체가 어떤 프로퍼티를 갖고 있는지를 따진다.
let a : object = {
b : 'x'
}
a.b // 에러 TS2399 : 'b' 프로퍼티는 object에 존재하지 않음
object는 서술하는 값에 관한 정보를 거의 알려주지 않으며, 값 자체가 자바스크립트 객체이자 null이 아니라고만 말해줄 뿐이다.
let a = {
b : 'x'
} // {b : string}
a.b // string
let a : {b : number} {
b : 12
} // {b : number}
위는 객체 리터럴 문법이다. 타입스크립트가 a의 형태를 추론하게 하거나 중괄호 안에서 명시적으로 타입을 묘사할 수 있다.
- 객체를 const로 선언할 때의 타입추론 : 자바스크립트 객체의 값은 바뀔 수 있으며, 타입스크립트도 객체를 만든 . 후필드 값을 바꾸려 할수 있다는 사실을 알기에 다른 기본타입과 달리 더 좁은 타입으로 추론하지 않는다.
let a : {b : nmumber}
a = {} // 에러 TS2741 : '{}'타입에는 {b : nmumber} 타입에 필요한 'b'가 없음
a = {
b : 1,
c : 2 // 에러 TS2322 : '{b : number; c : number}'타입을 {b : nmumber}에 지정할 수 없음
}
- 확싫한 할당 : 변수를 선언하고 나중에 초기화 하는 상황에서 타입스크립틑 변수를 사용하기 전에 값을 할당하도록 강제한다.
타입스크립트는 객체 프로퍼티에 엄격한 편이다. 예를 들어 객체에 number 타입의 b라는 프로퍼티가 있어야 한다고 정의하면 b가 없거나 다른 추가 프로퍼티가 있으면 에러를 발생시킨다.
let a : {
b : number
c? : string
[key : number] : boolean
}
- a는 number 타입의 프로퍼티 b를 포함한다.
- a는 string 타입의 프로퍼티 c를 포함할 수도 있다.
- a는 boolean 타입의 값을 갖는 number 타입의 프로퍼타를 여러 개를 포함할 수 있다.
a = {10 :true} // 에러 TS2741 : {10 : true} 타입에는 'b' 프로퍼티가 없음
a = {b : 1, 33 : 'red'} // 에러 TS2741 : 'string'타입은 'boolean' 타입에 할당할 수 없음
- 인덱스 시그니쳐 : [key : T] : U 와 같은 문법은 타입스크립트에 어떤 객체가 여러 키를 가질 수 있음을 알려준다. 명시적으로 정의한 키 외에 다양한 키를 객체에 안전하게 추가할 수 있다. 인덱스 시그니쳐의 키 T 는 반드시 number나 string 타입에 할당할 수 있는 타입이어야한다. 키 이름은 원하는 이름을 가져다 바꿔도 된다. 예시) let airplaneSeatingAssignments : {[seatNumber : string] : string}
필요한 경우 readonly 한정자를 이용해 특정 필드를 읽기 전용으로 정의할 수 있다. 객체 리터럴 표기법에는 빈 객체 타입({})이라는 특별한 상황이 존재한다. null과 undefined를 제외한 모든 타입은 빈 객체 타입에 할당할 수 있으나 사용하기 까다롭기에 피하는 것이 좋다.
마지막으로 객체 : Object로 객체 타입을 만드는 방법도 있다. {}과 비슷한 방법이며 마찬가지로 가능하면 사용하지 않아야 한다. 객체를 정의하는 방법은 다음과 같이 네 가지로 요약할 수 있다.
- 객체 리터럴 또는 형태라 불리는 표기법({a : string}). 객체가 어떤 필드를 포함할 수 있는지 알고 있거나 객체의 모든 값이 같은 타입을 가질 떄 사용한다.
- 빈 객체 리터럴 표기법({}). 이 방법믄 비추천한다.
- object 타입. 어떤 필드를 가지고 있는지는 관심 없고, 그저 객체가 필요할 때 사용한다.
- Object 타입. 이 방법은 비추천한다.
3.2.9 타입 별칭, 유니온, 인터섹션
값 뿐만 아니라 타입에도 어떤 동작을 수행 할 수 있다.
타입 별칭
변수를 선언해서 값 대신 변수로 칭하듯이 타입 별칭으로 타입을 가리킬 수 있다.
type Age = number
type Person = {
name : string
age : Age
}
타입스크립트는 별칭을 추론하지 않으므로 반드시 별칭의 타입을 명시적으로 정의해야한다.
let age : Age = 55
let driver : Person = {
name : 'James May',
age : age
}
Age는 number의 별칭이므로 number에도 할당할 수 있다.
let age = 55
let driver : Person = {
name : 'James May',
age : age
}
정리하면 위 코드처럼 바꿀 수 있다.
자바스크립트 변수 선언과 마찬가지로 하나의 타입을 두번 정의할 수는 없다. let과 const처럼 타입 별칭에도 블록 영역이 적용된다. 내부에 정의한 타입 별칭이 외부의 정의를 덮어쓴다. 타입 별칭은 복잡한 타입을 DRY하지 않도록 해주며 변수가 어떤 목적으로 사용되었는지 쉽게 이해할 수 있게 도와준다. 값을 변수로 할당할지를 결정하는 것과 같은 기준으로 타입 별칭을 사용할지 여부를 결정할 수 있다.
유니온과 인터섹션 타입
타입스크립트는 타입에 적용할 수 있는 특별한 연산자인 유니온(|)과 인터섹션(&)을 제공한다. 타입은 집합과 비슷하므로 집합처럼 연산을 수행할 수 있다.
type Cat = {name : string, purrs : boolean}
type Dog = {name : string, bark : boolean, wags : boolean}
type CarOrDogOrBothh = Cat | Dog
type CatAndDog = Cat & Dog
// Cat
let a : CarOrDogOrBoth = {
name : 'Bonkers',
purrs : true
}
// Both
a = {
name : 'Donkers',
barks : true,
purrs : true,
wags : true
}
let b : CatAndDog = {
name : 'Domino',
barks : true,
purrs : true,
wags : true
}
유니온 타입 (|)에 사용된 값이 꼭 유니온을 구성하는 타입 중 하나일 필요는 없으며 양쪽 모두에 속할 수 있다.
실전에서는 대개 인터섹션보다 유니온을 자주 사용한다.
function trueOrNull(isTrue : boolean) {
if(ifTrue){
return 'true'
}
return null
}
이 함수는 string 또는 null을 반환할 수 있다. 이를 다음처럼 표현할 수 있다.
type Returns = string | null
다음 함수 예제를 보자
function(a : string, b : number){
return a || b
}
조건이 참이면 반환타입이 string이고 그렇지 않으면 number다. 즉, string | number를 반환한다.
3.2.10 배열
타입스크립트 배열도 연결, 푸시, 검색, 슬라이스 등을 지원하는 특별한 객체이다.
let a = [1,2,3] // number[]
var b = ['a','b'] // string[]
let c : string[] = ['a'] // string[]
let d = [1, 'a'] // (numbner | string)[]
const e = [2, 'b'] // (numbner | string)[]
let f = ['red']
f.push('blue')
f.push(true) // 에러 TS2345 : 'true' 타입 인수를 'string' 타입 매개변수에 할당할 수 없음
let g = [] // any[]
g.push(1) // number[]
g.push('red') // (string | number)[]
let h : number[] = [] // number[]
h.push(1) // number[]
h.push('red') // 에러 TS2345 : 'red' 타입 인수를 'number' 타입매개변수에 할당할 수 없음
대개 배열을 동형으로 만든다. 즉. 한 배열에 모든 항목이 같은 타입을 갖도록 설계하려 노력한다. 그렇지 않으면 타입스크립트에 배열과 관련한 작업이 안전한지 증명해야 하므로 추가 작업을 해야 한다.
예제 f를 보면 왜 동열 배열의 처리가 쉬운지 알 수 있다. 배열을 선언하고 문자열 타입의 값을 추가했을 때 타입스크립트는 이 배열이 문자 값을 갖는 배열이라 추론한다. 'blue'는 문자열이므로 아무 문제없이 추가되지만 true를 추가하려하면 에러가 발생한다. 반면 d는 초기화하면서 number와 string을 저장했으므로 타입스크립트는 d의 타입을 number | string으로 추론한다.객체와 마찬가지로 배열을 const로 만들어도 타입스크립트는 타입을 더 좁게 추론하지 않는다.
g는 특별한 상황으로, 빈 배열로 초기화 하면 타입스크립트는 배열의 요소타입을 알 수 없으므로 any일 것으로 추측한다. 배열을 조작하여 요소를 추가하면 타입스크립트가 주어진 정보를 이용해 배열의 타입을 추론한다. 배열이 정의된 영역을 벗어나면(예 : 함수 안에서 배열을 선언하고 이를 반환) 타입스크립트는 배열을 더 이상 확장할 수 없도록 최종타입을 할당한다.
3.2.11 튜플
튜플은 길이가 고정되어 있고, 각 인덱스의 타입이 알려진 배열의 일종이다. 다른 타입과 달리 튜플은 선언할 떄 타입을 명시해야 한다. 자바스크립트에서 배열과 튜플에 같은 대괄호을 사용하는데 타입스크립트에서는 대괄호를 배열 타입으로 추론하기 때문이다.
let a : [number] = [1]
// [이름, 성씨, 생년] 튜플
let b : [string, string, number] = ['malcolm','gladwell',1963]
b = ['queen', 'elizabeth', 'ii', 1962] // 에러 TS2322 : 'string'은 'number'타입에 할당할 수 없음
튜플은 선택형 요소도 지원한다. 객체 타입에서와 마찬가지로 ?는 선택형을 뜻한다.
// 방향에 따라 다른 값을 갖는 기차 요금 배열
let trainFares : [number, number?][] = [
[3.75],
[8.75, 7.70],
[10.50]
]
// 다음과 같음
let moreTrainFares : ([number] | [number, number])[] = [
// ...
]
또한 튜플이 최소 길이를 갖도록 지정할 때는 나머지 요소(...)를 사용할 수 있다.
let friends : [string, ...string[]] = ['Sara', 'Tail', 'Chloe', 'Claire']
// 이형 배열
let list : [number, boolean, ...string[]] = [1, false, 'a', 'b', 'c']
읽기 전용 배열과 튜플
타입스크립트는 readonly 배열 타입을 기본을 지원하므로 이를 이용해 볼변 배열을 바로 만들 수 있다. 읽기 전용 배열은 명시적 타입 어노테이션으로 만들 수 있다. 읽기 전용 배열을 갱신하려면 .push, .slice처럼 내용을 바꾸는 동작 대신 .concat, .slice같이 내용을 바꾸지 않는 메서드를 사용해야 한다.
let as : readonly number[] = [1,2,3] // readonly number[]
let bs : readnoly numner[] = as.concat(4) // readonly number[]
let three = bs[2] // number
as[4] = 5 // 에러 TS2542 : 'readonly number[]'의 인덱스 시그니쳐 타입은 읽기만 허용함
as.push(6) // 에러 TS2339 : 'push' 프로퍼티는 'readonly number[]' 타입에 존재하지 않음
타입스크립트는 Array처럼 읽기 전용 배열과 튜플을 만드는 긴 형태의 선언 방법을 지원한다.
type A = readonly string[] // readonly string[]
type B = ReadonlyArray<string> // readonly string[]
type C = Readonlt<string[]> // readonly string[]
type D = readonly [number, string] // readonly [number, string]
type E = Readonly<[number, string]> // readonly [number, string]
읽기 전용 배열은 스프레드(...)나 .slice 등으로 배열을 조금만 바꿔도 우선 배열을 복사해야 하므로, 주의하지 않으면 응용 프로그램의 성능이 느려질 수 있다.
3.2.12 null, undefined, void, never
자바스크립트는 null, undefined 두 가지 값으로 부재를 표현한다. 타입스크립트도 두 가지 값 모두를 지원한다. 타입스크립트에서 undefined 값의 타입은 오직 undefined 뿐이고 null 값의 타입은 null 뿐이라는 점에서 특별한 타입이다. 두 값은 조금 다른데 undefined는 아직 정의하지 않았음을 의미하는 반면 null은 값이 없다는 의미다.
타입스크립트는 이외에도 void와 never 타입도 제공한다. void는 명시적으로 아무것도 반환하지 않는 함수의 반환타입을 가리키며 never는 절대 반환하지 않는 함수 타입을 가리킨다.
// (a) nnumber 또는 null을 반환하는 함수
function a (x : number){
if(x<10){
return x
}
return null
}
// (b) undefined를 반환하는 함수
function b() {
return undefined
}
// (c) void를 반환하는 함수
function c() {
let a = 2 +2
let b = a * a
}
// (d) never를 반환하는 함수
function d() {
throw TypeError('I always error')
}
// (e) never를 반환하는 함수
function e() {
while (true) {
dosomething()
}
}
- a은 null를 , b는 undefined를 명시적으로 반환한다.
- c는 undefined를 반환하지만 명시적인 return문을 사용하지 않았으므로 void를 반환한다고 말할 수 있다.
- d는 예외를 던진다
- e는 영원히 실행되며 반환하지 않았므로 반환타입이 never라 할 수 있다.
unknown이 모든 타입의 상위 타입이라면 never는 모든 타입의 서브타입이다. 즉, 모든 타입에 never를 할당할 수 있으며 never 값은 어디서든 안전하게 사용할 수 있다.
타입 | 의미 |
null | 값이 없음 |
undefined | 아직 값을 변수에 할당하지 않음 |
Void | return문을 포함하지 않는 함수 |
never | 절대 반환하지 않는 함수 |
- 엄격한 null 확인
예전 버전의 타입스크립트 또는 strictNullCheck 옵션을 false인 경우에서는 null이 조금 다르게 작동한다. 이때 null은 never를 제외한 모든 타입의 하위 타입이다. 즉, 모든 타입이 null이 될 수 있으므로 모든 값이 null인지 아닌지 먼저 확인하지 않고는 타입이 무엇이라고 단정할 수 없다. 실무에서는 이는 굉장히 불편한 일이므로 보통 이과정을 생략한다. 그리고 예상치 않은 상황에서 값이 null이라면 런타임에 치명적인 널 포인터 예외가 발생한다.
3.2.13 열거형
열거형은 해당 타입으로 사용할 수 있는 값을 열거하는 기법이다. 열거형은 키를 값에 할당하는, 순서가 없는 자료구조다. 키가 컴파일 타임에 고정된 객체라고 생각하면 쉽다. 따라서 타입스크립트는 키에 접근할 떄 주어진 키가 실제 존재하는 지 확인할 수 있다.
enum Language {
English,
Spanish,
Russain
}
타입스크립트는 자동으로 열거형의 각 멤버에 적절한 숫자를 추론해 할당하지만, 값을 명시적으로 설정할 수도 있다.
enum Language {
English = 0,
Spanish = 1,
Russain = 2
}
점 또는 괄호 표기법을 열거형 값에 접근할 수 있다.
let myFirstLanguage = Language.Russian // Language
let mySecondLanguage = Language['English'] // Language
열거형을 여러 개로 나눠 저의한 다음 타입스크립트가 이들을 합치도록 할 수도 있다. 타입스크립트는 여러 열거형 정의 중 한 가지 값만 추론할 수 있으므로 열거형을 분할할 때 주의해야 하며, 각 열거형 멤버에 명시적을 값을 할당하는 습관을 기르는 것이 좋다. 계산된 값을 사용할 수도 있으므로 모든 값을 정의할 필요는 없다.(빠진 값은 타입스크립트가 추론한다.)
enum Language {
English = 100,
Spanish = 200 + 300,
Russain // 501로 추론
}
열거형에 문자열 값을 사용하거나 문자열과 숫자 값을 혼합할 수 있다. 타입스크립트에서는 값이나 키로 열거형에 접근할 수 있도록 허용하지만 이는 불안정한 결과를 초래하기 쉽다.
let a = Color.Red // Color
let b = Color.Green // 에러 TS2339 : 'Green' 프로퍼티는 'typeof Color'타입에 존재하지 않음
let c = Color[255] // stirng
let d = Color[6] // string !!! << 에러가 발생하지 않고 실행된다
더 안전한 열거형 타입인 const enum을 이용하면 타입스크립트가 이런 안전하지 않은 작업을 막도록 만들 수 있다.
const enum Language {
English ,
Spanish ,
Russain
}
// 유요한 enum 키 접근
let a = Language.English // Language
// 유요하지않은 enum 키 접근
let b = Language.Taglog // 에러 TS2339: 'Taglog' 프로퍼티는 'typeof Language'타입에 존재하지 않음
// 유요한 enum 키 접근
let c = Language[0] // 에러 TS2476: const enum 멤버는 문자열 리터럴로만 접근할 수 있음
// 유요하지 않은 enum 키 접근
let d = Language[6] // 에러 TS2476: const enum 멤버는 문자열 리터럴로만 접근할 수 있음
const enum은 기본적으로 아무 자바스크립트도 생성하지 않으며 그 대신 필요한 곳에 열거형 멤버의 값을 채워 넣는다. (타입스크립트는 Language.Spanish가 사용된 모든 코드를 값 1로 바꾼다.)
- TSC 플래그 : preserveConstEnums
누군가의 타입스크립트 코드에 정의된 const enum을 가져왔을 때는 이 채워 넣기 기능이 문제를 일으킬 수 있다. 개발자가 타입스크립트 코드를 컴파일한 이후에 열거형을 만든 사람이 자신의 const enum을 갱신하면 런타임에 같은 열거형이 버전에 따라 다른 값을 갖게 되고, 타입스크립트가 이 상황에서 할 수 있는 일은 없다.
const enum을 사용할 때는 채워 넣기 기능을 되도록 피해야 하며 제어할 수 있는 타입스크립트 프로그램에서만 사용해야한다. NPM으로 배포하거나 라이브러리로 제공할 프로그램에서는 const enum를 사용하지 말아야한다.
const enum의 런타임 코드 생성을 활성화할려면 tsconfing.json 파일에서 preserveConstEnums TSC 설정을 true로 바꾼다.
const enum Flippable {
Burger,
Chair,
Cup,
Skateboard,
Table
}
function flip(f : Filppable) {
retrun 'flipped it'
}
filp(Flippable.Chair) // 'flipped it'
filp(12) // 'flipped it' !!!! <<< 에러가 발생해야하지만 실행된다
위 예제처럼 타입스크립트 할당규칙 때문에 생긴 운이 나쁜 결과로 에러가 발생하지 않고 실행될 수 있다. 문자열 값을 갖는 열거형을 사용해 해결할 수 있다. 결과적으로 숫자 값을 받는 열거형은 전체 열거형의 안정성을 해칠 수 있다.
const enum Flippable {
Burger = 'Burger',
Chair = 'Chair',
Cup = 'Cup',
Skateboard = 'Skateboard',
Table = 'Table'
}
function flip(f : Filppable) {
retrun 'flipped it'
}
filp(Flippable.Chair) // 'flipped it'
filp(12) // 에러 TS2345: '12' 인수 타입은 'Flippable' 매개변수 타입에 할당할 수 없음
filp('Hat') // 에러 TS2345: 'Hat' 인수 타입은 'Flippable' 매개변수 타입에 할당할 수 없음
- 열거형을 안전하게 사용하는 방법은 까다로우므로 열거형 자체를 멀리할 것을 권한다. 타입스크립트에는 열거형을 대체할 수단이 많다.
3.3 마치며
타입스크립트가 값의 타입을 추론하도록 하거나 값의 타입을 명시할 수 있다. let과 var를 사용하면 일반적인 타입으로 추론하는 반면, const를 이용하면 더 구체적인 타입을 추론하게 만든다. 구체적 타입은 보통 일반 타입의 서브 타입이다.
타입 | 서브 타입 |
Boolean | 볼 리터럴 |
bigint | 큰 정수 리터럴 |
number | 숫자 리터럴 |
string | 문자열 리터럴 |
symbol | unique symbol |
object | 객체 리터럴 |
Array | 튜플 |
enum | const enum |
'Javascript > Typescript' 카테고리의 다른 글
Typescript Programming : 07. 에러처리 (0) | 2024.06.14 |
---|---|
Typescript Programming : 06. 고급 타입 (0) | 2024.06.07 |
Typescript Programming : 05. 클래스와 인터페이스 (0) | 2024.05.31 |
Typescript Programming : 04. 함수 (1) | 2024.05.24 |
Typescript Programming : 02. 타입스크립트 내려다보기 (0) | 2024.05.08 |