타입스크립트로 계산기를 만들어보며, 배웠던 점을 정리하고자 한다.
타입스크립트는, 타입을 추론한다. 이때, 우리가 타입을 더 잘 알고 있다고 판단이 들면, type assertion을 통해, 이를 알릴 수 있다. 타입을 표명하면, 타입스크립트가 추론한 타입이 아닌, 사용자가 표명한 타입을 받아들인다. 이때 실제로 해당 타입이 아니더라도, 타입스크립트가 받아들이기 때문에, 아주 안전한 방법은 아니다.
사용법: value as type
예시
const $result = document.querySelector("#result") // 타입스크립트는, querySelector('#result')가 반환하는 값의 타입이 'Element'라 판단한다.
const $result = document.querySelector('#result') as HTMLElement; // querySelector('#result')가 반환하는 값의 타입은 HTMLElement임을 표명할 수 있다.
객체 타입을 생성할 때, 타입의 프로퍼티들을 미리 다 알 수 없지만, 프로퍼티들이 어떤 형태를 가져야 하는지는 알 수도 있다. 이때, index signature를 사용하여, 형태를 알릴 수 있다. index signature 파라미터의 타입은 string, number, symbol 혹은 template literal일 수 있다.
사용법: [signature_parameter: type]: type
예시
type Person = {
[index: string]: string;// Person타입 객체 프로퍼티의 key가 string이라면, value도 string이어야만 한다.
name: string;
sex: string;
height: number; // key가 string일 때, string이 아닌 값을 가질 수 없다.
3: number; // 여기서 3은 숫자가 아니라 string이다. 그래서 value도 string이어야 한다.
}
유의할 점은, 시그니처 파라미터의 타입을 string으로 하면, 위의 예시에서와 같이 key의 타입이 number일 때도 적용되지만, 역방향은 적용되지 않는다. 즉 시그니처 파라미터의 타입을 number로 하면, key의 타입이 string일 때 적용되지 않는다.
type Person = {
[key: number]: number;
name: string;
sex: string;
}
let myPerson: Person = {
name: 'Ben', // 파라미터의 타입이 number이기 때문에, key가 string일 때 value가 number가 아니여도 된다.
sex: 'Male',
3: 'foo', // 에러가 발생한다.
}
유니온과 함께 사용할 수 있다.
예시
type Person = {
[index: string]: string | number;// Person타입의 객체의 프로퍼티는, key가 string일 때, value는 string혹은 number이어야 한다.
name: string;
sex: string;
height: number; // 에러가 발생하지 않는다.
}
또한 readonly와 함께 사용할 수 있다.
예시
type Person = {
readonly [index: string]: string | number
name: string;
sex: string;
}
let myPerson: Person = {
name: "Ben",
sex: "Male",
height: "unknown",
}
myPerson.name = 'Jenny'; // 에러가 발생하지 않는다.
myPerson.sex = "Female"; // 에러가 발생하지 않는다.
myPerson.height = 'tall' // 에러가 발생한다. readonly가 적용된 것. name과 sex는 타입 별칭 생성 시에 포함돼있어서, readonly가 적용되지 않는 것.
keyof 연산자는, 객체 타입을 받고, 키의 문자열 혹은 숫자 리터럴 유니온을 생성한다.
사용법: keyof 객체_타입
예시
type Person = {name:string; sex: string}
type foo = keyof Person; // 이는 type foo = 'name' | 'sex' 와 동일하다.
많은 경우, DRY한 코드를 위해, 다른 타입을 확장하여 사용하고 싶을 수 있다. mapped types는 이때 유용하다. 문법은, index signature의 문법과 매우 유사하다.
사용법: [key in 유니온_타입]: type
예시
type Operator = '+' | '-' | '/' | '*';
type Operations = {
[key in Operator]: Function;
}
let myOperations: Operations = {
'+': 34, // 에러. 함수가 할당돼야 함.
'-' : 24, // 에러
'/' : 10, // 에러
'*' : 55, // 에러
}
위에서 설명한 keyof와 같이 사용할 수 있다.
예시
let Operator = {
'+': 0,
'-': 1,
'/': 2,
'*': 3,
}
type Operations = {
[key in keyof typeof Operator]: Function;
}
let myOperations: Operations = {
'+': ()=>{console.log('lol')},
'-': ()=>{console.log('lol')},
'/': ()=>{console.log('lol')},
'*': ()=>{console.log('lol')},
}