오늘의 명언
해당 포스트 목적
타입스크립트와 관련해서 수집한 내용을 모두 정리하기 위한 모음집으로 사용하기 위해 작성되었다. 이는 수정사항이나 추가 내용이 존재하면 새로 갱신되어 업로드 할 예정이다.
유니온 타입과 인터섹션 타입
type 키워드를 사용할 때 주로 활용되는 부수적인 타입으로 유니온 타입과 인터섹션 타입이 있다. 이는 자바스립트에서 OR 연산자나 AND 연산자 역할을 하는 각각의 ||, && 과 유사한 역할을 한다.
유니온 타입( A | B) | 둘 중 하나라도 만족하는 경우 타입 검사를 통과 시킴
유니온 타입은 두 가지의 타입이 존재하면 그 중 하나의 타입과 일치하는 경우 타입 검사를 통과시키고자 할 때 사용한다. 즉, 아래와 같이 사용할 수 있다.
type Value = number | string;
const value :Value = 5;
const value :Value = 'fsds';
그러나 number 나 string 과 같이 명시된 타입이 아닌 null, undefined 등의 타입 값을 넣으려고 하면 에러가 발생한다.
인터섹션 타입( A & B) | 두 타입 모두 만족하는 경우만 타입 검사를 통과 시킴
유니온 타입과 다른게 인터섹션 타입은 지정한 두 타입을 모두 만족해야 검사를 통과할 수 있다. 즉, 아래와 같이 작성하는 경우 두 번째 케이스의 경우에는 name 이 number 타입이므로 에러를 발생시킨다.
type Value = {age:number} & {name: string};
const value :Value = {age: 15, name: 'Jhon'}; // 통과
const value :Value = {age: 15, name: 500}; // 에러
여기서 알아두고 갈 점은 인터섹션 타입의 경우 각각 명시한 타입을 서로 병합한다는 점이다. 앞서 type Value = {age:number} & {name: string}; 이렇게 선언한 타입의 경우는 {age: number, name: string} 과 동일하다.
타입스크립트의 내장타입
설명 없음. 맛보기 코드 스니펫
// 기본 타입
let num: number = 10;
let str: string = "Hello";
let bool: boolean = true;
let symbol: symbol = Symbol('uniqueKey');
let n: null = null;
let u: undefined;
// 객체 타입
let person: { name: string; age: number } = { name: 'John Doe', age: 30 };
// 배열 타입
let numbers: number[] = [1, 2, 3, 4, 5];
let names: string[] = ['Alice', 'Bob', 'Charlie'];
// 튜플 타입
let point: [number, number] = [10, 20];
// 유니언 타입
let value: number | string = 10; // 또는 'Hello'
// 인터섹션 타입
let user: { name: string; age: number; email: string } & { isAdmin: boolean } = {
name: 'Jane Doe',
age: 35,
email: 'jane.doe@example.com',
isAdmin: true
};
// 제네릭 타입
function identity<T>(value: T): T {
return value;
}
let numIdentity = identity(10); // numIdentity는 number 타입
let strIdentity = identity('Hello'); // strIdentity는 string 타입
// 도구 타입
type PartialUser = Partial<{ name: string; age: number; email: string }>; // 일부 속성만 정의 가능한 타입
타입 주장(Type Assertion) | 타입 추론이 애해모호한데 개발자가 해당 변수나 값의 타입을 확신할 때 사용( ex. value as string : value 은 string 타입임 확신함!)
타입 주장은 타입스크립트의 추론 기능에 의해 any 타입으로 추론이 되거나, 개발자가 의도하지 않은 타입으로 유추가 되는 경우 개발자가 해당 값이나 변수의 타입을 확신할 때 이를 타입스크립트 컴파일러에게 확신을 주장하는 방법이다.
예를 들어, 아래 value 은 any 타입을 가지고 있지만, value 이 string 타입임을 확신할 때 value as string 이라 입력하면 타입스크립트 컴파일러는 해당 value이 string 타입이라고 믿어줄게 하며 넘어가준다.
let value: any = "Hello, TypeScript!";
let length: number = (value as string).length;
이 방식의 문제점은 개발자가 확신하지 못하는 경우에도 as 를 남발할 때 타입스크립트는 이 또한 허용한다는 점이다. 그래서 잘못된 방식으로 남용하게 되면 any 타입과 다를바 없으므로 꼭 필요할 때만 사용하는게 좋다.
Type 과 Interface
보통 유형 구분없이 함수, 변수, 튜플, 유니온, 교차, 리터럴 등등 타입을 지정할 때 Type 키워드, 객체나 함수의 타입을 명시할 떄 Interface 를 주로 사용한다. 이 둘은 모두 기능적으로 타입을 지정할 때 사용하지만, 차이점이 존재한다.
Type | 모든 유형에 타입 지정이 가능하지만, 확장이 유연하지 못하다.
어떤 유형이든 타입 지정 가능
우선 type 은 아래와 같이 어떤 유형의 값이든 타입 지정이 가능하다.
type MyEnumType = "A" | "B" | "C";
type MyTupleType = [number, string, boolean];
type MyUnionType = string | number;
type MyLiteralType = "Hello World";
extends 를 통한 확장이 불가능하고, & 을 통한 결합은 가능함
하지만, type의 단점은 interface 와 다르게 extends 를 통한 확장이 불가능하다는 점이다. 이것이 불편한 이유는 아래와 같이 MyType2 를 확장하려면 기존 MyType1 에 & (교차 연산자)를 사용하여 별도의 타입을 선언해주어야 한다. 만일 복잡한 구조의 객체라면 지정하기도 까다롭겠지만, 관리나 추가적인 확장도 꺼려질 수도 있다.
type MyType1 = number;
// MyType1을 확장하려면 새로운 type을 선언해야 합니다.
type MyType2 = MyType1 & {
name: string;
};
중복 선언 불가능
또한 type 은 중복으로 타입 선언이 불가능하다. 즉, 아래와 같이 작성한다면 동일한 타입명으로 사용하지 못하게 오류가 발생한다.
type MyType = number;
type MyType = string; // 오류 발생
Interface | extends 를 통한 확장이 용이하지만 객체 타입에 한해서만 타입 지정이 가능하다
extends 를 통한 타입 확장이 쉬우나 객체로 취급되는 함수나 객체 리터럴 등에만 적용 가능
inteface 는 type 과 다르게 extends 를 사용해서 타입을 손쉽게 확장할 수 있다. 그러나 타입이 객체로 인정되는 객체 리터럴이나 함수에서만 사용 가능하다는 제한점이 있다.
interface Person {
name: string;
age: number;
}
interface Student extends Person {
studentId: number;
major: string;
}
동일한 이름의 인터페이스가 있으면 서로 병합해줌
그럼에도 interface 의 가장 좋은 점은 type 과는 다르게 동일한 인터페이스명으로 생성하여도 에러가 발생하지 않고, 두 인터페이스 간에 타입을 병합한다는 점이다. 이로 인해, extends 말고도 유연한 타입 확장이 가능하다
interface Point {
x: number;
y: number;
}
interface Point { // 이전 선언과 병합됩니다.
z: number;
}
타입 가드 | 런타임에 변수 타입을 확인 후 해당 타입으로 좁히기 위한 방법
아래 예시와 같이 any 타입으로 어떤 유형의 값이든 전달받을 수 있는 value가 있다. 하지만, 함수 내부적으로 별도의 타입을 체크하여 아무 값이나 반환되지 못하도록 막을 수 있으며, 서로 다른 로직을 처리하게 할 수 있다. 이렇게 광범위한 타입을 몇 가지로 줄여나가기 위한 방법 타입가드라고 부른다.
function isNumber(value: any): value is number {
return typeof value === "number";
}
function process(value: any) {
if (isNumber(value)) {
return value * 2;
} else {
return "Not a number!";
}
}
타입 별칭 | 기존에 선언된 타입에 새로운 이름을 부여하는 것
아래 코드에서 x는 number 타입을 지정하고, y도 number 타입을 지정하였다. 하지만 이들 타입에 Point 라고 이름을 명명하여 이를 distance 함수의 매개변수에 재사용하고 있는데, 이 때 Point 와 같은 기존 타입의 명칭을 대체하는 것을 타입 별칭이라 한다.
type Point = { x: number; y: number };
function distance(p1: Point, p2: Point): number {
return Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2));
}
제네릭 타입 | 타입 정의 없이 동적으로 타입을 지정할 수 있는 타입
함수에서 사용 예시
아래의 경우에는 타입을 명시하지 않아도 전달되는 인자에서 타입을 유추하여 T에 담긴다.
function reverseArray<T>(array: T[]): T[] {
return array.reverse();
}
const numbers = [1, 2, 3, 4, 5];
const reversedNumbers = reverseArray(numbers); // [5, 4, 3, 2, 1]
const strings = ['apple', 'banana', 'orange'];
const reversedStrings = reverseArray(strings); // ['orange', 'banana', 'apple']
명시적으로 타입을 지정하려면 아래와 같이 <> 를 활용한다. 아래 예시에 따르면 <string> 이 <T> 에 전달되어 T 는 string 타입으로 유추된다.
function identity<T>(arg: T): T {
return arg;
}
let result = identity<string>("Hello, TypeScript!");
참고로 제네릭의 경우도 타입 별칭을 적용할 수 있다.
type User<T> = {
name: T
}
함수에서 제네릭 타입의 허용 속성을 제한하려면 | extends
extends 를 통해 타입을 상속해주면 T 는 해당 타입 외에의 값들은 받지 못하게 된다. 예를 들어, 제한된타입이 {age: string} 이라면 T 는 age 가 string 인 경우만 처리할 수 있고, 일치하지 않으면 타입 불일치 에러를 띄운다.
function 함수이름<T extends 제한된타입>(매개변수: T): 반환타입 {
// 함수 본문
}
클래스에서 사용 예시
클래스의 인스턴스를 생성하고, new Box<타입>( ); 을 지정하는 경우 지정한 타입에 해당하는 값만 클래스 내부에 전달할 수 있다. 또한, 아래 예시 처럼 <K, V> 와 같이 여러개의 제네릭 타입 매개변수 지정도 가능하다.
class KeyValuePair<K, V> {
private key: K;
private value: V;
constructor(key: K, value: V) {
this.key = key;
this.value = value;
}
getKey(): K {
return this.key;
}
getValue(): V {
return this.value;
}
}
// 여러 개의 다른 타입을 가진 KeyValuePair 인스턴스 생성
const numberPair = new KeyValuePair<string, number>('age', 30);
const stringPair = new KeyValuePair<string, string>('name', 'John');
const booleanPair = new KeyValuePair<string, boolean>('isValid', true);
const objectPair = new KeyValuePair<string, object>('data', { key: 'value' });
유틸리티 타입
Partial<Type> | 정의된 속성을 모두 선택적(옵셔널)으로 만드는 타입
// 모든 속성이 선택적인 Partial<User> 타입
type PartialUser = Partial<User>;
const partialUser: PartialUser = {}; // 유효
const partialUser2: PartialUser = { name: 'John' }; // 유효
파셜 타입은 아래와 같이 지정한 것과 같다
interface Car {
make?: string; // string | undefined
model?: string; // string | undefined
year?: number; // number | undefined
}
Required<Type> | 타입이 지정한 모든 속성을 필수로( 누락을 허용하지 않는다)
interface Car {
make?: string;
model?: string;
year?: number;
}
// 모든 속성이 필수인 Required<Car> 타입
type RequiredCar = Required<Car>;
const requiredCar: RequiredCar = { make: 'Toyota', model: 'Camry', year: 2024 }; // 유효
const requiredCar2: RequiredCar = { make: 'Honda' }; // 오류: model 및 year가 필요합니다.
Readonly<Type> | 타입이 적용된 모든 속성을 읽기 전용으로
interface Point {
x: number;
y: number;
}
// 모든 속성이 읽기 전용인 Readonly<Point> 타입
type ReadonlyPoint = Readonly<Point>;
const point: ReadonlyPoint = { x: 10, y: 20 };
point.x = 5; // 오류: 읽기 전용입니다.
Record<Keys, Type> | 관련성 있는 키-값 구조를 이루는 데이터를 사전 형태로 시각화하고자 할 때 사용
type Weekday = 'Monday' | 'Tuesday' | 'Wednesday' | 'Thursday' | 'Friday';
// 각 요일에 대한 정보를 담는 레코드 타입
type WeekdaySchedule = Record<Weekday, string>;
const schedule: WeekdaySchedule = {
Monday: 'Gym in the evening',
Tuesday: 'Dinner with friends',
Wednesday: 'Work late',
Thursday: 'Movie night',
Friday: 'TGIF! Party time'
};
Pick<Type, Keys> | 정의된 타입 중 일부 타입만 선택적으로 사용할 때 적용
interface Person {
name: string;
age: number;
address: string;
}
// 이름과 나이만 선택하는 새로운 타입
type BasicInfo = Pick<Person, 'name' | 'age'>;
const basicInfo: BasicInfo = { name: 'Alice', age: 30 }; // 유효
const basicInfo2: BasicInfo = { name: 'Bob', address: '123 Main St' }; // 오류: address가 없습니다.
'나만의 모음집' 카테고리의 다른 글
[모음집] NestJS(with TypeORM + PostgreSQL) 예제 모음집 (1) | 2024.05.17 |
---|---|
나중에 참고할 수도 있는 코드 모음집 (0) | 2024.05.08 |
[모음집] Zustand 모음집 (0) | 2024.05.06 |
[모음집] IT 용어 모음집 (0) | 2024.05.06 |
[나만의 모음집] VSCODE 사용자 코드 조각 아카이브 (0) | 2024.04.30 |