- "TypeScript is JavaScript with syntax for types", JavaScript 는 기본적으로 동적 타입의 언어로 대부분의 에러를 코드를 실행했을 때만 확인이 가능하므로 타입체크를 런타임이 아닌 빌드 타임에 정적으로 수행하도록 한다. 타입스크립트는 자바스크립트의 슈퍼셋으로 함수의 변환 타입, 배열, enum 등 기존에는 사용하기 어려웠던 타입 관련 작업을 쉽게 처리하도록 도움을 줌.
function test(x, y) {
return x / y
}
test(10, 2) //5
test('X', 'Y') // NaN
/* type check */
function testWithTypeCheck(x, y) {
if(typeof x !== 'number' || typeof y !== 'number') {
throw new Error('invalid Type Error')
}
return x / y
}
testWithTypeCheck('X', 'Y') // Uncaught Error: invalid Type Error
/* TypeScript */
function testWithTypeScript(x: number, y: number) {
return x / y
}
testWithTypeScript('X', 'Y')
/* JavaSciprt로 트랜스파일하면 Argument of type 'string' is not assignalbe to
parameter of type 'number' 에러가 발생.
*/
- test 함수는 a,b가 숫자일 경우만 사용이 가능하고 그 외의 경우에는 원하지 않는 결과를 반환할 수 있다. testWithTypeCheck 함수처럼 모든 함수와 변수에 typeof 를 적용하면 비효율적이므로 TypeScript로 작성한 testWithTypeScript 함수처럼 간결하게 사용 가능.
- 타입스크립트는 변수에 타입을 설정하여 해당 변수에 타입에 맞는 값을 할당할 수 있게 되므로 런타임이 아닌 빌드하는 시점에 에러가 발생할 코드에 대해 확인이 가능하다.
- .ts 또는 .tsx로 작성된 파일은 자바스크립트로 변환되어 Node.js 또는 브라우저 같은 런타임 환경에서 실행.
타입스크립트의 활용법
: 타입을 엄격하게, 적극적으로 사용하냐에 따라 효용성에 큰 차이가 나타난다.
● any 대신 unknown을 사용하자
function Func (callback: unknown) { if (typeof callback === 'function') { callback() return } throw new Error('callback is assignable to type function') }- any 를 사용하면 타입스크립트가 제공하는 정적 타이핑의 이점을 모두 상실하는 것과 같으므로 top type인 unknown과 type narrowing을 사용하여 의도한 타입으로 좁혀 사용해야 한다.
- top type과 반대되는 bottom type인 never 타입은 어떠한 인수 값도 받지 않는다는 의미. 예를 들어 외부 함수에서는 'a' 라는 인수 값을 필수로 받고, 외부 함수를 상속받아 새로운 함수를 정의할 때 해당 인수 값을 사용하지 않을 경우 사용한다.
● 타입가드를 사용하자
- 타입가드와 조건문을 사용하여 변수와 함수의 타입을 효과적으로 의도한 타입으로 사용이 가능
instanceof / typeof/* instanceof 지정한 instance가 특정 class의 instance인지 확인 가능한 연산자 */ class AuthError extends Error { constructor() { super() } get message() { return 'Failed authorization' } } class unExpectedError extends Error { constructor() { super() } get message() { return 'UnExpected Error' } } async function fetchList () { try { const response = await fetch('/api/list') return await response.json() } catch (e) { if(e instanceof AuthError) { //... } if(e instanceof unExpectedError) { //... } throw e } } /* typeof 는 특정 요소의 자료형 확인 시 사용 */ function check (value: string | undefined) { if (typeof value === 'string') { //... } if (typeof value === 'undefined') { //... } }- instanceof 예제의 에러의 e의 타입은 unknown, 에러에 대해 타입 가드를 통해 instance를 확인하여 원하는 처리가 가능.
- typeof 예제는 특정 요소에 대해 자료형 확인하여 원하는 처리가 가능.
ininterface Cat { name: string } interface Dog { age: number } function check (animal: Cat | Dog) { if('name' in animal) { animal.name } if('age' in animal) { animal.age } }- animal은 Cat 또는 Dog가 될 수 있으며 in을 활용하여 특정 객체에만 있는 값을 확인. 조건문으로 두 객체에 겹치지 않는 프로퍼티를 확인하면 해당 변수가 어떤 타입으로부터 들어오고 있는지 확인이 가능하다.
generic
-단일 타입이 아닌 다양한 타입에 대응이 가능.function finedFirstAndLast (list: unknown[]) { return [list[0], list[list.length - 1]] } const [first, last] = finedFirstAndLast([0,1,2,3,4]) // typeof first unknown // typeof last unknown /* function with generic */ function finedFirstAndLast<T>(list: T[]): [T, T] { return [list[0], list[list.length - 1]] } const [first, last] = finedFirstAndLast([0,1,2,3,4]) // typeof first number // typeof last number const [first, last] = finedFirstAndLast(['0','1','2','3','4']) // typeof first string // typeof last string
- T 라는 제너릭을 선언해 각 배열의 요소와 반환 값의 요소로 사용해 다양한 타입을 처리하는 함수로 생성.
index signaturetype Animal = { [key: string]: string } const cat: Animal = { name: 'kit', age: '19', } cat['name'] // kit cat['age'] // 19- [key: string]이 index signature로 키에 원하는 타입 부여가능. 존재하지 않는 key로 접근 시에는 undefined를 반환되므로 객체의 키는 동적으로 선언보다는 타입을 정의하여 사용하는 것이 좋음.
/* Record<key, Value> Example*/ type Animal = Record<'name'|'age', string> const cat: Animal = { name: 'kit', age: '5', } /* type & index signature Example */ type Animal = {[key in 'name' | 'age']: string} const cat: Animal = { name: 'kit', age: '5', }- Record<Key, Value> 객체의 타입에 원하는 키와 값을 할당할 수 있고, type 과 index signature를 사용하면 객체를 원하는 타입으로 선언이 가능.
Object.keys(animal).map((key) => { // Object.keys(animal) 반환타입 string[] // string으로 animal의 인덱스 키로 접근 불가 return animal[key] }) /* 해결 방법 1. 반환 타입을 강제 */ function keyOf<T extends Object>(obj: T): Array<keyof T> { return Array.from(Object.keys(obj)) as Array<keyof T> } keyOf(animal).map((key) => { return animal[key] }) /* 해결방법 2. 가져온 key 타입을 강제 */ Object.keys(animal).map((key) => { return hello[key as keyof Animal] })- Object.keys의 반환타입이 string []으로 강제 된 이유는 'One of TypeScript's core principles is that type checking focused on the shape that values have. This is sometimes called "duck typing" or "structual subtyping" .' 즉, 타입 스크립트의 핵심 원칙 중 하나는 타입 검사는 해당 값이 가진 형태를 중요하게 여긴다. 이를 "duck typing" 또는 "구조적 서브타입" 이라 한다.
type Car = { name: string } type Truck = Car & { speed: number } function horn(car: Car) { console.log(`${car.name} 빵빵`) } const truck: Truck = { name: "truck", speed: 60, } horn(truck) // Car type에 필요한 속성을 충족하므로 유효- JavaScript는 객체의 타입에 구애받지 않으므로 이러한 특징에 맞춰 모든 키가 들어올 수 있어 string[] type으로 제공.