타입 : 값과 이 값으로 할 수 있는 일의 집합
타입스크립트의 타입 계층
[출처] https://www.oreilly.com/library/view/programming-typescript/9781492037644/ch03.html
타입 종류
any
: 타입들의 대부
: any로 무엇이든 할 수 있지만 꼭 필요한 상황이 아니라면 사용하지 않는 것이 좋음
=> 값이 자바스크립트처럼 동작하므로 타입스크립트를 사용하는 의미가 없어짐
: 꼭 필요하다면 any를 명시적으로 선언하는 것이 좋음
let a: any = 666 // any
let b: any = ['danger'] // any
let c = a + b // any
💚 TSC 플래그: noImplicitAny
: 암묵적인 any가 나타났을 때 예외를 일으키고 싶다면 활성화
: stric을 활성화 했다면 따로 활성화할 필요 x
unknown
: 타입을 미리 알 수 없는 어떤 값이 있을 때 any 대신 사용
: 모든 값을 대표하지만, unknown의 타입을 검사해 정제하기 전까지는 타입스크립트가 해당 값을 사용할 수 없게 강제함.
정제 방법: typeof, instanceof
지원 연산: 비교 연산(==, ===, ||, &&, ?), 반전(!)
let a: unknown = 30 // unknown
let b = a === 123 // boolean
let c = a + 10 // ERROR: 객체의 타입이 'unknown'
if (typeof a === 'number') {
let d = a + 10 // number
}
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 // Error: 'false' 타입을 'true' 타입에 할당할 수 없음
=> c, e: 타입리터럴
🧡 타입 리터럴
: 오직 하나의 값을 나타내는 타입
number
: 모든 숫자(정수, 소수, 양수, 음수, Infinity, NaN 등)
지원 연산: 숫자 관련 연산(+, - , %, < 등)
💚 긴 숫자를 처리할 떄는 숫자 분리자를 이용해 숫자를 읽기 쉽게 만들 수 있음
=> 타입과 값 모두에 사용 가능
let oneMillion = 1_000_000 // 1000000과 같음
let twoMillion: 2_000_000 = 2_000_000
bigint
: 자바스크립트와 타입스크립트에 새로 추가된 타입
: 라운딩 관련 에러 걱정 없이 큰 정수 처리 가능
: number는 253까지의 정수 표현 가능, bigint는 이보다 큰 수도 표현 가능
let a = 1234n // bigint
const b = 5678n // 5678n
var c = a + b // bigint
let d = a < 1234 // boolean
let e = 88.5 // Error: bigint 리터럴은 반드시 정수여야 함
let f: bigint = 100n // bigint
let g: 100n = 100n // 100n
let h: bigint = 100 // Error: '100' 타입은 'bigint' 타입에 할당할 수 없음
string
: 모든 문자열의 집합
symbol
: ES2015에 새로 추가된 기능
: 실무에서는 자주 사용하지 않는 편, 객체와 맵에서 문자열 키를 대신하는 용도로 사용
let a = Symbol('a') // symbol
let b: symbol = Symbol('b') // symbol
var c = a === b // boolean
let d = a + 'x' // Error: '+' 연산을 'symbol' 타입에 적용할 수 없음
const e = Symbol('e') // typeof e
const f: unique symbol = Symbol('f') // typeof f
let g: unique symbol = Symbol('f') // Error: 'unique symbol' 타입은 반드시 'const'여야 함
let h = e === e // boolean
let i = e === f // Error: 'unique symbol' 타입은 서로 겹치는 일이 없으므로 결과는 항상 'false'
- Symbol('a')는 주어진 이름으로 새로운 symbol을 만든다는 의미
- 만들어진 symbol은 고유하여 다른 symbol과 == 또는 ===로 비교했을 때 같지 않다고 판단
객체
: 객체의 형태를 정의
- 자바스크립트의 객체는 구조 기반 타입을 갖도록 설계되어 있음
: 객체의 이름에 상관없이 객체가 어떤 프로퍼티를 갖고 있는지를 따짐
// 방법1)
let a: object = {
b: 'x'
}
a.b // Error: 'b' 프로퍼티는 'object'에 존재하지 않음
// 방법2) - 추론
let a = {
b: 'x'
} // {b: string}
a.b // string
let b = {
c: {
d: 'f'
}
} // {c: {d: string}}
// 방법3) - 객체 리터럴
let a: { b: number } = {
b: 12
} // {b: number}
class Person {
constructor(public firstName: string, public lastName: string) {}
}
c = new Person('matt', 'smith')
🤎 선택형 프로퍼티
let a: {
b: number
c?: string
}
a = {b: 1}
b = {b: 1, c: undefined}
c = {b: 1, c: 'd'}
// 예시
let airplaneSeatingAssignments: {
[seatNumber: string]: string
} = {
'34D': 'Boris Cherny',
'34E': 'Bill Gates'
}
🤎 인덱스 시그니처
: 이 객체에서 모든 T 타입의 키는 U 타입의 값을 가짐
[key: T]: U
// 예시
let airplaneSeatingAssignments: {
[seatNumber: string]: string
} = {
'34D': 'Boris Cherny',
'34E': 'Bill Gates'
}
🤎 읽기 전용 프로퍼티
: 이 객체에서 모든 T 타입의 키는 U 타입의 값을 가짐
let user: {
readonly firstName: string
} = {
firstName: 'abby'
}
user.firstName // string
user.firstName = 'abbey with an e' // Error: 'firstName'은 읽기 전용 프로퍼티이므로 할당할 수 없음
배열
let a = [1, 2, 3] // number[]
var b = ['a', 'b'] // string[]
let c: string[] = ['a'] // string[]
let d = [1, 'a'] // (string | number)[]
const e = [2, 'b'] // (string | number)[]
let g = [] // any[]
g.push(1) // number[]
g.push('red') // (string | number)[]
function buildArray() {
let a = [] // any[]
a.push(1) // number[]
a.push('x') // (string | number)[]
return a
}
let myArray = buildArray() // (string | number)[]
튜플
: 배열의 서브타입
: 길이가 고정됨, 각 인덱스의 타입이 알려진 배열의 일종
: 서언할 때 타입을 명시해야함
let a: [number] = [1]
let b: [string, string, number] = ['kang', 'kim', 1988]
// 선택형 요소
let trainFares: [number, number?][] = [
[3.75],
[8.25, 7.70],
[10.50]
]
// 위와 같음
let moreTrainFares: ([number] | [number, number])[] = [
...
]
// 최소 길이 지정: 나머지 요소(...)
// 최소 한 개의 요소를 갖는 string 배열
let friends: [string, ...string[]] = ['Sara', 'Tali', 'Chloe', 'Claire']
// 이형 배열
let list: [number, boolean, ...string[]] = [1, false, 'a', 'b', 'c']
읽기 전용 배열과 튜플
: 읽기 전용 배열을 갱신하려면 .push, .splice처럼 내용을 바꾸는 동작 대신 .concat, .slice 같이 내용을 바꾸지 않는 메서드를 사용해야 함
let as: readonly number[] = [1, 2, 3] // readonly number[]
let bs: readonly number[] = as.concat(4) // readonly number[]
let three = bs[2] // number
as[4] = 5 // Error: 'readonly number[]'의 인덱스 시그니처 타입은 읽기만 허용
as.push(6) // 'push' 프로퍼티는 'readonly number[]' 타입에 존재하지 않음
type A = readonly string[] // readonly string[]
type B = ReadonlyArray<string> // readonly string[]
type C = Readonly<string[]> // readonly string[]
type D = readonly [number, string] // readonly [number, string]
type E = Readonly<[number, string]> // readonly [number, string]
null, undefined, void, never
null
: 값이 없음
=> 값을 계산하려 하면 에러 발생
undefined
: 아직 정의하지 않았음
void
: 명시적으로 아무것도 반환하지 않는 함수의 반환 타입
ex) console.log
function a() {
throw TypeError('I always error')
}
function b() {
while(true) {
doSomething()
}
}
💙 unknown이 모든 타입의 상위 타입이라면 never는 모든 타입의 서브타입
열거형
: 해당 타입으로 사용할 수 있는 값을 열거하는 기법
: 키를 값에 할당하는, 순서가 없는 구조
: 각 멤버에 명시적으로 값을 할당하는 습관을 기르는 것이 좋음
enum Language {
English,
Spanish,
Russian
}
let myFirstLanguage = Language.Russian // Language
let mySecondLanguage = Language['English'] // Language
// 타입스크립트가 자동으로 열거형의 각 멤버에 적절한 숫자를 추론해 할당하지만,
// 값을 명시적으로도 설정 가능
enum Language {
English = 0,
Spanish = 4,
Russian // 타입스크립트가 4 다음 숫자인 5로 추론
}
// 열거형에 문자열 값을 사용하거나 문자열과 숫자 값을 혼합 가능
enum Color {
Red = '#c10000',
Blue = '#009ac1',
Pink = 0xc10050, // 16진수 리터럴
White = 255 // 10진수 리터럴
}
let a = Color.red // Color
let b = Color.Green // Error: 'Green' 프로퍼티는 'typeof Color' 타입에 존재하지 않음
let c = Color[255] // string
let d = Color[6] // string !!! 접근할 수 없어야 하지만 타입스크립트는 접근을 허용함!!!
// const enum: 문자열 리터럴로만 접근가능
// 위와 같은 안전하지 않은 작업을 막을 수 있음
const enum Language {
English,
Spanish,
Russian
}
let a = Language.English // Language
let b = Language.Tagalog // Error: 'Tagalog' 프로퍼티는 'typeof Language' 타입에 존재하지 않음
let c = Language[0] // Error: const enum 멤버는 문자열 리터럴로만 접근할 수 있음
let d = Language[6] // Error: const enum 멤버는 문자열 리터럴로만 접근할 수 있음
💙 숫자값을 받는 열거형은 전체 열거형의 안전성을 해칠 수 있음
💙 열거형을 안전하게 사용하는 방법은 까다로우므로 열거형 자체를 멀리 할 것을 권함
type Age = number
type Person = {
name: string
age: Age
}
let age: Age = 55
let driver: Person = {
name: 'James May',
age: age
}
type Cat = {name: string, purrs: boolean}
type Dog = {name: string, barks: boolean, wags: boolean}
type CatOrDogOrBoth = Cat | Dog
let a: CatOrDogOrBoth = {
// Cat, Dog 타입의 프로퍼티를 모두 가질 수 있음
}
type Cat = {name: string, purrs: boolean}
type Dog = {name: string, barks: boolean, wags: boolean}
type CatAndDog = Cat & Dog = {
// name 프로퍼티만을 가질 수 있음
}