요즘 javascript 취업시장에 typescript는 필수가 되었다.
이는 런타임에서 버그를 예방하기 위함이다.
하지만 제대로 인지를 못하고 사용하면
쓰는 이만 못하기 때문에 꼭 제대로 알고 있자!
(타입 종류, interface type 차이)
(이 내용은 필자의 생각이며 잘 못 된 부분이나 개인적인 의견이
있다면 자유롭게 댓글 달아줘)
✔️ Summary
- Enum
- extends, keyof, infer
- Generic
- 내장 Conditional type
- 프로젝트에 도입하면 좋은 것
헷깔리는 것
- as : 타입을 강제하는 것
- infer : 타입을 추론하는 것
- !: : null 또는 undefined가 아니라고 단언하는 것
- in : 객체의 속성(프로퍼티)이 존재하는지 확인할 때 사용
- is : 해당 타입이 맞는지 런타임 전에 체크 ex) value is number
- instanceof : 객체가 특정 클래스 또는 생성자 함수의 인스턴스인지 확인할 때 사용
✔️ Enum ???
enum의 특징
- 일정 수의 상수로 구분되는 집합, 기본값 0에서 시작
- 특정 수를 제한 하고 싶을 때 사용
문자형, 숫자형 열거형 사용시 주의
- 숫자형 표현식 값이 NaN 이거나 Infinity 이면 컴파일 시점에 오류
- 숫자형의 기본값은 0이고 할당을 안 해주면 자동으로 1, 2 …로 세팅
- 열거형 자체에서 프로퍼티로 모든 멤버에 접근하며, 열거형의 이름을 사용해 타입을 선언
- 문자형 열거형에서 각 멤버들은 문자열 리터럴 또는 다른 문자열 열거형의 멤버로 상수 초기화 해야 함
- 문자형은 값이 불안정한 경우 사용
- 같이 사용하는 경우(문자+숫자) 런타임에서 장점을 취하려는 것이 아니라면 이렇게 사용하지 않는것을 권장함
enum DirectionString {
MIDDLE, // 0으로 할당
UP = "up",
DONW = "down",
RIGHT = "right",
CENTER, // error
LEFT = "left",
CENTER, // error
}
enum을 왜 사용하지 말라고 하는지
값 변경 못하고 추가도 못함
key value 바꿔서 사용가능(reverse mapping 가능)
enum은 객체에 비해 트랜스파일 될 때 코드량이 많음 (reverse mapping 때문)
//enum
enum WeekdaysEnum {
Monday = 1,
Tusesday=2,
}
var Weekdays;
(function (Weekdays) {
Weekdays[Weekdays["Monday"] = 1] = "Monday";
Weekdays[Weekdays["Tusesday"] = 2] = "Tusesday";
})(Weekdays || (Weekdays = {}));
//객체
const WeekdaysConst = {
Monday = 1,
Tusesday=2,
} as const
✔️ extends, keyof, infer
typeof
개체 유형을 사용하고 해당 키의 문자열 또는 숫자 리터럴 합집합을 생성
//'x' | 'y'
type Point = { x: number; y: number };
type P = keyof Point;
//'x' | 'y'
let Point = { x: 1, y: 2 };
type P = keyof typeof Point;
//number | string
let Point = { x: 1, y: 's' };
type P = typeof Point[keyof typeof Point];
extends
T extends U 가 있을 때 T는 U에 포함되는 T ≤ U 로 생각하면 됨
64 extends number//true
number extends 64//false
string[] extends any//true
string[] extends any[]//true
never extends any//true
any extends any//true
infer
잘 이해가 안 되어서 쉬운 예시를 GPT에게 물어봄
쉬운 예시 : 우리가 공장에서 상자를 생산하는 라인을 가정해봅시다. 이때, 상자에는 다양한 물건들을 담을 수 있습니다. 하지만 라인을 따라서 상자를 만들 때, 어떤 물건이 담길지 미리 알지 못합니다. 이때, 상자를 만들기 위해 사용되는 물건의 종류를 자동으로 판단하고 추론하는 장치가 있다면 유용할 것입니다. 여기서 'infer'는 이러한 장치와 비슷한 역할을 합니다.
type Box<T> = {
item: T;
};
type Unbox<T> = T extends Box<infer U> ? U : never;
const stringBox: Box<string> = { item: "Hello, TypeScript!" };
const numberBox: Box<number> = { item: 42 };
type UnboxedString = Unbox<typeof stringBox>; // 'string'
type UnboxedNumber = Unbox<typeof numberBox>; // 'number'
const str: UnboxedString = "Hello, TypeScript!";
const num: UnboxedNumber = 42;
✔️ 제네릭 Generic
특징
- 원하는 타입에 맞게 로직을 구성할 수도 있음
- 객체 인스턴스를 통합하고 싶을 때 사용함
활용 예시
함수의 파라미터로 사용 예제
function getText<T>(text: T): T {
return text;
}
getText<string>('hi');
getText<number>(10);
getText<boolean>(true);
3항으로 조건문 처리
//예시 타입
type StringContainer = string | string[]
type NumberContainer = number | number[]
//기본 3항
type ItemType<T> = {
id:T
pw:T extends string ? StringContainer : NumberContainer
}
//never로 else 처리
type ItemTypeNever<T> = {
id:T extends string | number ? T : never
pw:T extends string ? StringContainer
: T extends number ? NumberContainer : never
}
const item : ItemType<string> = {
id:'asdsasd',
pw:['qwe','qweqwe']
}
배열 filter
//array filter
type ArrayFilter<T> = T extends any[] ? T : never
type StringArray = ArrayFilter<string | string[]>//string[]
객체 인스턴스 통합
class Person{
name:string;
}
class Employee extends Person{
department:number;
}
//오류없음
class Animal{
name:string;
}
const work:Array<Person> = [new Person(), new Employee(),new Animal()];
work.map((person) => {
if(person instanceof Person) {
return '사람'
}
})
interface 통합
//예시 타입
interface People {
id:string
pw:string[]
}
interface Employee {
id:number
pw:number
}
//통합 인스턴스
interface World {
getInfo<T extends string|number>(id:T):T extends string ? People : Employee
}
let world:World = null as any
const people = world.getInfo('123123')
const employee = world.getInfo(123123)
배열 객체 Flatten
//T === [] -> number[] | number , T === {} -> key[] , else -> T
type Flatten<T> = T extends any[] ?
T[number] : T extends object ?
T[keyof T] : T;
const numbers = [1, 2, 3]
//number[]?
type NumberArrayFlattened = Flatten<typeof numbers>
const obj = {
name: 'qwe',
age: 20
}
// name | age , T[name] | T[age] -> string | number
type ObjectFlattened = Flatten<typeof obj>
const bool = true
//true
type BoolFlatten = Flatten<typeof bool>
함수 return 타입
type ReturnTypeOfFunction<T> = T extends (...args: any) => infer R ? R : any;
function add(a: number, b: number): number {
return a + b;
}
function greet(name: string): string {
return `Hello, ${name}!`;
}
type AddReturnType = ReturnTypeOfFunction<typeof add>; // number
type GreetReturnType = ReturnTypeOfFunction<typeof greet>; // string
Promise 객체 확인
type UnpackPromise<T> = T extends Promise<infer K>[] ? K : any;
const promise = [Promise.resolve("Mark"), Promise.resolve(38)]
type Expected = UnpackPromise<typeof promise>//string | number
✔️ ReadonlyArray<T>, as const 차이점
ReadonlyArray<T>
- 타입스크립트에서 제공하는 내장 타입으로, 배열의 모든 요소를 읽기 전용
- 배열을 선언할 때 타입 뒤에 []를 붙여서 사용
- 배열의 요소를 수정할 수 없게 만들어, 불변성을 강제하는 역할
- 배열에만 사용
- 배열의 모든 타입 추출, 각각은 안됨
const numbers: ReadonlyArray<number> = [1, 2, 3];
numbers[0] //readonly number[]
numbers.push(4); // error
numbers[0] = 10; // error
as const
- 타입스크립트에서 리터럴 타입(string, number)을 사용하는데 도움을 주는 문법
- 변수 또는 객체의 값 뒤에 as const를 붙여서 사용
- 해당 값을 수정할 수 없게 만들어 불변성을 강제하는 역할
- 변수나 객체의 값에 사용
- 각 요소의 타입 추출 가능
const person = {
name: "John",
age: 30,
} as const;
person.name = "Alice"; //error
const weekdays = ["Monday", "Tuesday", "Wednesday"] as const;
weekdays[0] //Monday
weekdays.push("Thursday"); //error
DeepReadonly
type DeepReadonly<T> = T extends (infer R)[]
? ReadonlyArray<DeepReadonly<R>>
: T extends object
? { readonly [K in keyof T]: DeepReadonly<T[K]> }
: T;
const obj = {
name: "John",
age: 30,
address: {
city: "New York",
postalCode: "10001",
},
};
const arr = [{ name: "Alice" }, { name: "Bob" }];
const deepReadonlyObj: DeepReadonly<typeof obj> = obj;
const deepReadonlyArr: DeepReadonly<typeof arr> = arr;
deepReadonlyObj.name = "Jane";//error
deepReadonlyObj.address.city = "Los Angeles";//error
deepReadonlyArr.push({ name: "Charlie" });//error
✔️ 내장 Conditional type
Partial<User>
제공된 타입의 모든 속성을 선택적으로 만드는 타입
즉, 모든 속성이 선택사항이 되어 해당 속성을 포함하지 않아도 되는 상
interface User {
name: string;
age: number;
email: string;
}
type PartialUser = Partial<User>;
const partialUser: PartialUser = {};
const partialUser2: PartialUser = { name: "John" };
Required<User>
제공된 타입의 모든 속성을 필수로 만드는 타입
모든 속성이 반드시 포함되어야 하는 상태가 됩
interface User {
name?: string;
age?: number;
email?: string;
}
type RequiredUser = Required<User>;
const requiredUser: RequiredUser = {
name: "Alice",
age: 30,
email: "alice@example.com"
};
Readonly<User>
제공된 타입의 모든 속성을 읽기 전용으로 만드는 타입
한 번 값을 할당하면 수정할 수 없는 상태가 됩
interface User {
name: string;
age: number;
}
type ReadonlyUser = Readonly<User>;
const user: ReadonlyUser = { name: "Bob", age: 25 };
user.name = "Alice";
Record<K, T>
주어진 키 타입(K)에 해당하는 모든 속성을 값 타입(T)으로 매핑하는 타입
type Weekday = "Monday" | "Tuesday" | "Wednesday" | "Thursday" | "Friday";
type WeekdaySchedule = Record<Weekday, string>;
const schedule: WeekdaySchedule = {
Monday: "Work from home",
Tuesday: "Meeting at 3 PM",
Wednesday: "Gym at 6 PM",
//..
};
Pick<T, K>
제공된 타입(T)에서 특정 키 타입(K)에 해당하는 속성만 선택하여 새로운 타입을 만드는 타입
interface User {
name: string;
age: number;
email: string;
address: string;
}
type UserBasicInfo = Pick<User, "name" | "email">;
const userBasicInfo: UserBasicInfo = {
name: "John",
email: "john@example.com"
};
Omit<T, K>
제공된 타입(T)에서 특정 키 타입(K)에 해당하는 속성을 제외하고 새로운 타입을 만드는 타입
interface User {
name: string;
age: number;
email: string;
address: string;
}
type UserWithoutAddress = Omit<User, "address">;//2개 제외시 |
const userWithoutAddress: UserWithoutAddress = {
name: "Alice",
age: 30,
email: "alice@example.com",
};
Exclude<T, K>
제공된 타입(T)에서 특정 타입(K)에 해당하는 유니온 타입을 제외한 새로운 타입을 만드는 타입
type NumberOrString = number | string;
type OnlyNumber = Exclude<NumberOrString, string>;
const num: OnlyNumber = 42;
NonNullable<T>
제공된 타입(T)에서 null 또는 undefined를 제거한 새로운 타입을 만드는 타입
type NullableString = string | null | undefined;
type NonNullableString = NonNullable<NullableString>;
function printMessage(message: NonNullableString) {
console.log(message);
}
printMessage("Hello, TypeScript!"); //Hello, TypeScript!
printMessage(null);//error
Parameters<T>
제공된 함수 타입(T)의 매개변수 타입들을 튜플로 가져오는 타입
type AddFunc = (a: number, b: number) => number;
type AddFuncParams = Parameters<AddFunc>;
function add(a: number, b: number): number {
return a + b;
}
const params: AddFuncParams = [10, 5];
const result = add(...params); // 15
Constructor<T>
클래스 타입(T)의 생성자 함수 타입을 가져오는 타입
class Person {
constructor(public name: string, public age: number) {}
}
type PersonConstructor = Constructor<Person>;
const personConstructor: PersonConstructor = Person;
const personInstance = new personConstructor("Alice", 30);
console.log(personInstance); // Person { name: 'Alice', age: 30 }
InstanceType<T>
제공된 클래스 타입(T)의 인스턴스 타입을 가져오는 타입
class Car {
constructor(public brand: string) {}
}
type CarInstance = InstanceType<typeof Car>;
const carInstance: CarInstance = new Car("Toyota");
console.log(carInstance); // Car { brand: 'Toyota' }
ReadonlyArray<T>
제공된 타입(T)의 모든 속성을 읽기 전용 배열로 만드는 타입
interface Todo {
id: number;
task: string;
}
type ReadonlyTodoArray = ReadonlyArray<Todo>;
const todos: ReadonlyTodoArray = [
{ id: 1, task: "Buy groceries" },
{ id: 2, task: "Clean the house" }
];
// 아래 코드는 오류 발생
todos.push({ id: 3, task: "Go for a walk" });
todos[0].task = "Buy fruits";
✔️ 참고 링크
https://www.youtube.com/live/ViS8DLd6o-E?si=ETqGXVRUC07q_XxA
'공부(Study) > 언어(JS, TS)' 카테고리의 다른 글
자바스크립트 일급객체 프로토타입 한번에 정리! (0) | 2023.12.06 |
---|---|
Date 객체의 정의와 쓰면 안되는 이유 (1) | 2023.12.05 |
typescript 기초! 이 정도는 알고 있어야 함! (0) | 2023.12.01 |
typescript Why?!?!?(이유, 주의, 활용) (0) | 2023.11.30 |
데이터 타입 구조 종류, JVM 자료구조, 입력과 출력시 (0) | 2021.12.22 |