1. 타입 추론이란?
let a = 10;
// number 타입으로 추론
- 타입스크립트는 타입이 정의되어 있지 않은 변수의 타입을 자동으로 추론한다. 이것을 타입 추론 이라고한다.
- 즉, 모든 변수에 일일이 타입을 정의하지 않아도 된다.
function func(param){ // 오류
}
- 하지만 모든 상황에 타입을 추론할 수 있는건 아니다. 함수의 매개변수 타입은 자동으로 추론할 수 없다.
- 타입 추론이 불가능한 변수에는 암시적으로 any 타입이 추론된다. → 엄격한 타입 검사 모드에서는 이런 any 타입의 추론을 오류로 판단한다.
2. 타입 추론이 가능한 상황들
2.1 변수 선언
let a = 10;
// number 타입으로 추론
let b = "hello";
// string 타입으로 추론
let c = {
id: 1,
name: "이정환",
profile: {
nickname: "winterlood",
},
urls: ["https://winterlood.com"],
};
// id, name, profile, urls 프로퍼티가 있는 객체 타입으로 추론
- 일반적인 변수 선언의 경우 초기값을 기준으로 타입이 추론된다.
- 복잡한 객체 타입도 추론된다.
2.2 구조 분해 할당
let { id, name, profile } = c;
let [one, two, three] = [1, "hello", true];
- 객체와 배열을 구조 분해 할당해도 추론된다.
2.3 함수의 반환값
function func() {
return "hello";
}
// 반환값이 string 타입으로 추론된다
- 함수의 반환값 타입은 return문을 기준으로 추론된다.
2.4 기본값이 설정된 매개변수
function func(message = "hello") {
return "hello";
}
- 기본값이 설정된 매개변수의 타입은 기본값을 기준으로 추론된다.
3. 주의할 점
3.1 암시적으로 any 타입으로 추론
let d;
// 암시적인 any 타입으로 추론
let d;
d = 10;
d.toFixed(); // number
d = "hello";
d.toUpperCase(); // string
d.toFixed(); // 오류
- 변수를 선언할 때 초기값을 생략하면 암시적인 any 타입으로 추론된다. 이때 매개변수의 타입이 암시적 any로 추론될 때와 달리 일반 변수의 타입이 any 타입으로 추론되는 상황은 오류로 판단하지 않는다.
- 변수에 값을 할당하면 그 다음 줄 부터는 any 타입이 해당 값의 타입으로 변한다.
- 이렇게 암시적으로 추론된 any 타입은 코드의 흐름에 따라 타입이 계속 변화하는데 이것을 any의 진화라고 표현하기도 한다.
3.2 const 상수의 추론
const num = 10;
// 10 Number Literal 타입으로 추론
const str = "hello";
// "hello" String Literal 타입으로 추론
- const로 선언된 상수도 타입 추론이 진행된다. 하지만 let 과는 다른 방식으로 추론된다.
- 상수는 초기화 때 설정한 값을 변경할 수 없기 때문에 가장 좁은 타입으로 추론된다.
3.3 최적 공통 타입
let arr = [1, "string"];
// (string | number)[] 타입으로 추론
- 다양한 타입의 요소를 담은 배열을 변수의 초기값으로 설정하면 최적의 공통 타입으로 추론된다.
4. 타입 단언이란?
type Person = {
name: string;
age: number;
};
let person: Person = {}; // 오류
person.name = "";
person.age = 23;
6let person = {} as Person; // ✅
7person.name = "";
8person.age = 23;
- 예제처럼 변수는 Person 타입으로 정의되었지만 초기화할 때는 빈 객체를 넣어두고 싶다는 가정을 한다면 이때 타입스크립트는 이를 허용하지 않는다. 빈 객체는 타입이 아니므로 오류를 발생시킨다.
- 이때 빈 객체를 Person 타입이라고 단언 해주면 오류를 방지할 수 있다.
- 값 as 타입
- 특정 값을 원하는 타입으로 단언할 수 있는 것을 타입 단언이라고 한다.
type Dog = {
name: string;
color: string;
};
let dog: Dog = {
name: "돌돌이",
color: "brown",
breed: "진도",
} as Dog
- 타입 단언은 초과 프로퍼티 검사를 피할 때 사용되기도 한다.
4.1 타입 단언의 조건
- 타입 단언은 두가지의 조건중 한가지는 반드시 만족해야한다. (값 as 타입)
- 값은 타입의 슈퍼 타입이다.
- 값은 타입의 서브 타입이다.
let num1 = 10 as never; // ✅
let num2 = 10 as unknown; // ✅
let num3 = 10 as string; // ❌
- never 타입은 모든 타입의 서브 타입이므로 number 타입의 서브 타입이다. 따라서 단언이 가능하다.
- unknown 타입은 모든 타입의 슈퍼 타입이므로 number 타입의 슈퍼 타입이다. 따라서 단언이 가능하다.
- string 타입은 number 타입과 서로 슈퍼-서브 타입의 관계를 갖지않으므로 단언이 불가능하다.
4.2 다중 단언
let num3 = 10 as unknown as string;
- 타입 단언은 다중 단언이 가능하다.
- 따라서 단언이 불가능 했던 string 타입을 단언할 수 있다.
💡 이러한 단언은 왼쪽에서 오른쪽으로 순서대로 이루어 진다.
- number 타입의 값을 unknown 타입으로 단언한다.
- unknown 타입의 값을 string 타입으로 단언한다.
즉, 중간에 값을 unknown 타입으로 단언하면 unknown 타입은 모든 타입의 슈퍼 타입이므로 모든 타입으로 다시 단언하는게 가능하다. 하지만 이런 단언 방식을 쓰는건 실제 해당 타입을 바꾸는게 아닌 눈속 임에 불과하기 때문에 필요한 경우에만 사용하는 것을 제외하고는 권장되지 않는다.
4.3 const 단언
let num4 = 10 as const;
// 10 Number Literal 타입으로 단언됨
let cat = {
name: "야옹이",
color: "yellow",
} as const;
// 모든 프로퍼티가 readonly를 갖도록 단언됨
- 타입 단언을 할때만 사용할 수 있는 const 타입이 존재한다.
- 특정 값을 const 타입으로 단언하면 마치 변수를 const로 선언한 것과 비슷하게 타입이 변경된다.
4.4 Non Null 단언
type Post = {
title: string;
author?: string;
};
let post: Post = {
title: "게시글1",
};
const len: number = post.author!.length;
- Non Null 단언은 값 as 타입 형태를 따르지 않는 단언이다.
- 값 뒤에 느낌표(!)를 붙여주면 값이 undefined, null이 아니라고 단언할 수 있다.
5. 타입 좁히기
function func(value: number | string) {
value.toFixed() // 오류
value.toUpperCase() // 오류
}
// 조건문 사용
function func(value: number | string) {
if (typeof value === "number") {
console.log(value.toFixed());
} else if (typeof value === "string") {
console.log(value.toUpperCase());
}
}
- 함수의 매개변수의 value 타입이 number 타입과 string 타입으로 되어있다고 value에서 타입에 해당하는 메서드를 사용하려고 하면 오류가난다.
- 만약 메서드를 사용하고 싶다면 조건문을 이용해서 해당 타입을 보장해줘야한다.
- 조건문을 이용해 조건문 내부에서 변수가 특정 타입임을 보장하면 해당 조건문 내부에서는 변수의 타입이 보장된 타입으로 좁혀진다. 이를 타입 좁히기 라고 표현한다.
- if(typeof === ..) 처럼 조건문과 함께 사용해서 타입을 좁히는 것들을 타입 가드라고 부른다. 즉, 타입 가드를 이용해서 타입을 좁힐 수 있다.
5.1 instanceof 타입가드
function func(value: number | string | Date | null) {
if (typeof value === "number") {
console.log(value.toFixed());
} else if (typeof value === "string") {
console.log(value.toUpperCase());
} else if (value instanceof Date) {
console.log(value.getTime());
}
}
- instanceof를 이용하면 내장 클래스 타입을 보장할 수 있는 타입가드를 만들 수 있다.
- 하지만 instanceof는 내장 클래스 또는 직접 만든 클래스에만 사용 가능한 연산이다. 개발자가 직접 만든 타입과 함께 사용할 수는 없다.
5.2 in 타입 가드
type Person = {
name: string;
age: number;
};
function func(value: number | string | Date | null | Person) {
if (typeof value === "number") {
console.log(value.toFixed());
} else if (typeof value === "string") {
console.log(value.toUpperCase());
} else if (value instanceof Date) {
console.log(value.getTime());
} else if (value && "age" in value) {
console.log(`${value.name}은 ${value.age}살 입니다`)
}
}
- 개발자가 직접 만든 타입과 사용하려면 in 연산자를 이용해야한다.
📌
한 입 크기로 잘라먹는 타입스크립트(TypeScript)
'Front-end > TypeScript' 카테고리의 다른 글
[TS] 제네릭에 대하여 (0) | 2024.02.21 |
---|---|
[TS] 함수와 타입 (0) | 2024.02.20 |
[TS] 타입 별칭(type) vs 인터페이스(interface) (1) | 2024.02.18 |
[TS] 타입은 집합이다? (1) | 2024.02.17 |
[TS] 타입스크립트의 기본 타입 (0) | 2024.02.16 |