타입 파라미터를 사용하여 코드 작성 시점에서는 구체적인 자료형을 정하지 않고, 사용 시점에 타입을 지정
<T>
형태로 많이 작성extends
를 사용하여 제한할 수도 있다. (인터페이스 확장의 extends와 반대됨): 타입의 모튼 프로퍼티를 옵셔널하게 만든다. (부분 집합 느낌)
interface Test {
name: string;
age: number;
}
type TestPartial = Partial<Test>;
: 타입의 모든 프로퍼티를 필수로 만든다
interface Test {
name?: string;
age?: number;
}
type TestRequired = Required<Test>;
: 타입의 모든 프로퍼티를 읽기 전용으로 만든다.
interface Test {
name: string;
age: number;
}
type TestReadonly = Readonly<Test>; // { readonly name: string; readonly age: string }
: Record<K, T> => K 키 집합으로부터 T 값의 객체 생성
: 지정된 프로퍼티만 가져온다
interface Test {
name: string;
age: number;
}
type TestPick = Pick<Test, 'name'>; // {name: string}
: 지정된 타입을 무시한다
interface Test {
name: string;
age: number;
}
type TestOmit = Omit<Test, 'name'>; // {age: number}
: 함수 타입의 매개변수를 가져온다.
function doSmth(value: string, anotherValue: number): string {
return 'test';
}
type Params = Parameters<typeof doSmth>; // {value: string, anotherValue: number}
: 함수의 반환 타입을 가져온다.
type Return = ReturnType<typeof doSmth>; // string
: 어떤 타입이 다른 타입과 일치하거나 확장하는지에 따라 타입을 조건부로 설정하는 것
type A = number extends number ? 'Yes' : 'No';
// 'Yes'
type B = string extends number ? 'Yes' : 'No';
// 'No'
: 조건부 타입 안에서 infer 키워드를 써서 새로운 타입 변수 선언이 가능하다
type Return<T> = T extends (...args: any[]) => infer R ? R : never;
type R1 = Return<() => number>; // number
type R2 = Return<(x:string) => Promise<boolean>>; // Promise<boolean>
: ?
를 사용하여 옵셔널한 요소를 튜플로 선언하고 나머지는 ...
을 사용하여 다른 타입에 따라 선언할 수 있다.
// 만약 튜플의 길이를 정확히는 모르지만 적어도 1 이상인 경우, `?`를 사용하여 선택적인 타입을 지정할 수 있습니다.
const list: [number, number?, boolean?] = [];
list[0] // number
list[1] // number | undefined
list[2] // boolean | undefined
list[3] // Type error: Tuple type '[number, (number | undefined)?, (boolean | undefined)?]' of length '3' has no element at index '3'.
// 기존 타입을 기반으로 튜플을 만들 수도 있습니다.
// 만약 배열의 시작 부분을 패딩하고 싶다면, rest 연산자 `...`를 사용하여 패딩할 수 있습니다.
function padStart<T extends any[]>(arr: T, pad: string): [string, ...T] {
return [pad, ...arr];
}
const padded = padStart([1, 2], 'test'); // [string, number, number]
튜플 : TS에서 길이와 각 원소의 타입이 고정된 배열
: 클래스와 그 안의 메서드는 추상적으로 선언하여 인스턴스화되지 않도록 할 수 있다.
abstract class Animal {
abstract makeSound(): void;
move(): void {
console.log('roaming the earth...');
}
}
// 확장 시 추상 메서드를 구현해야 합니다.
class Cat extends Animal {} // Compile error: Non-abstract class 'Cat' does not implement inherited abstract member 'makeSound' from class 'Animal'.
class Dog extends Animal {
makeSound() {
console.log('woof');
}
}
// 추상 클래스는 인터페이스처럼 인스턴스화할 수 없으며 추상 메서드도 호출할 수 없습니다.
new Animal(); // Compile error: Cannot create an instance of an abstract class.
const dog = new Dog().makeSound(); // "woof"
: 클래스 선언 외부에서 생성자 타입을 정의. 대부분의 경우 사용해서는 안된다.
: 클래스가 아닌 생성자 타입에서 생성자 매개변수를 가져오는 타입스크립트 헬퍼 함수이다.
: 튜플의 나머지 요소가 제네릭 일 수 있는 것. 여러 개의 rest 요소 사용이 허용된다.
declare function concatNew<T extends Arr, U extends Arr>(arr1: T, arr2: U): [...T, ...U];
: 튜플 요소의 이름을 [start: number, end: number]
와 같이 지정할 수 있다. 요소 중 하나에 이름이 지정된 경우, 모든 요소에 이름을 지정해야 한다.
type Foo = [first: number, second?: string, ...rest: any[]];
// 이렇게 하면 여기에서 인자의 이름을 올바르게 지정할 수 있으며 에디터에도 표시됩니다.
declare function someFunc(...args: Foo);
: 생성자에서 프로퍼티를 설정하면 타입을 유추할 수 있으므로 수동으로 설정할 필요가 없다.
class Animal {
// 생성자에서 타입을 할당할 때 타입을 설정할 필요가 없습니다.
name;
constructor(name: string) {
this.name = name;
console.log(this.name); // string
}
}
: JSDoc, TSDoc @deprecated
태그는 타입스크립트에서 인식된다
/** @deprecated Use `NewType` instead */
type OldType = string;
type NewType = string;
const a: OldType = "hello"; // IDE에서 경고: OldType은 deprecated됨
: 템플릿 리터럴을 사용하여 문자열 리터럴 타입을 조합/변형할 수 있는 기능
type ex = `Hello ${string}`;
// ex는 Hello로 시작하는 모든 문자열 타입
유니온 타입과 결합하여 조합이 가능하다.
type Lang = "ko" | "en";
type Path = `/api/${Lang}`;
// 결과: "/api/ko" | "/api/en"
제네릭, 다른 유틸리티와 결합이 가능하다.
type EventName<T extends string> = `on${Capitalize<T>}`;
type E = EventName<"click" | "hover">;
// "onClick" | "onHover"
// Capitalize : 문자열 리터럴 타입의 첫 글자를 대문자로 바꿔주는 유틸리티
: as
키워드를 사용해서 매핑된 타입의 키 이름을 다시 지정할 수 있다.
const obj = { value1: 0, value2: 1, value3: 3 };
const newObj: { [Property in keyof typeof obj as `_${Property}`]: number }; // { _value1: number; _value2: number; _value3: number; }
: 자기 자신을 참조하여 타입을 점진적으로 변환하거나 분해하는 방식, 자기 자신을 다시 호출하여 복잡한 타입 연산을 수행하는 기법
type Last<T extends any[]> =
T extends [...infer Rest, infer L]
? Last<Rest> extends never
? L
: Last<Rest>
: never;
// 결과
type A = Last<[1, 2, 3]>; // 3
type B = Last<['a', 'b', 'c', 'd']>; // 'd'
/// Last는 배열을 Rest와 마지막 L로 쪼갠 뒤, 재귀적으로 들어가면서 최종 L을 반환
: 에디터에서 @see variable/type/link
태그가 지원된다.
@see : 참고 장보를 제공할 때 사용됨
/**
* @see User
*/
type UserId = string;
interface User {
id: UserId;
name: string;
}
// UserId 볼 때 User 타입도 봐라
: ts 컴파일러에서 tsc -explainFiles
을 사용할 수 있다.
tsc -explainFiles
: 컴파일에 포함된 파일과 그 이유를 설명
: 구조 분해할 때 밑줄(-
)을 사용하여 변수를 사용하지 않을 것이라고 표시할 수 있음.
const [_first, second] = [3, 5];
console.log(second);
const [_, value] = [3, 5];
console.log(value);
: getter
와 setter
의 타입을 다르게 설정 가능
class Person {
private _name: string = "";
get name(): string {
return this._name;
}
set name(value: string | undefined) {
this._name = value ?? "";
}
}
const p = new Person();
p.name = undefined; // OK (setter 허용)
console.log(p.name.toUpperCase()); // OK (getter는 string 반환)
// 읽을때는 무조건 string로 다루고, 쓸 때는 undefined도 받을 수 있도록
: 클래스 상속 관계에서 부모 클래스의 메서드를 자식 클래스에서 재정의할 때 사용
class Animal {
makeSound() {
console.log("Some sound");
}
}
class Dog extends Animal {
override makeSound() {
console.log("Bark!");
}
}
const dog = new Dog();
dog.makeSound(); // Bark!
: static [propsName: string] : string
를 사용하여 정적 인덱스 시그니처를 설정할 수 있다.
type Roles = "admin" | "editor" | "viewer";
type RolePermissions = {
[R in Roles]: boolean; // 각 역할은 boolean 값
};
const perms: RolePermissions = {
admin: true,
editor: false,
viewer: true,
// superAdmin: false, // ❌ Roles에 없는 키는 에러
};
// [R in Roles] => 정적 인덱스 시그니처
// 키 집합이 미리 정의
인덱스 시그니처 : 객체가 어떤 키를 가질 수 있는지, 값의 타입이 무엇인지 동적으로 정의하는 방법
[key : string] : number
=> 모든 문자열 키에 대해 number 타입 값 허용
: jsDoc, tsDoc에서 @link
태그 지원
@link
: 코드나 문서, URL 등 외부 참조를 연결할 때 사용 (하이퍼링크 연결)