배워두면 유용한 타입스크립트의 여러 가지 기능을 알아보자.
TypeScript의 고급 타입을 사용하면 기존 타입을 기반으로 새로운 타입을 만들 수 있다.
Mapped Types과 Conditional Types을 활용해 타입을 변경하고 조작할 수 있으며, 이를 통해 코드의 유연성과 유지보수성을 높일 수 있다.
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
interface Point {
x: number;
y: number;
}
type ReadonlyPoint = Readonly<Point>;
Readonly는 제네릭 타입 T를 받아, 모든 속성을 읽기 전용으로 만드는 매핑된 타입이다.
ReadonlyPoint는 Point 인터페이스의 속성이 읽기 전용인 타입이다.
extends 키워드를 사용해 타입 제약을 추가.type NonNullable<T> = T extends null | undefined ? never : T;
NonNullable는 타입 T가 null 또는 undefined를 상속하면 never 타입을, 그렇지 않으면 T 타입을 반환한다.
interface Point {
x: number;
y: number;
}
type ReadonlyPoint = Readonly<Point>;
const regularPoint: Point = {
x: 5,
y: 10
};
const readonlyPoint: ReadonlyPoint = {
x: 20,
y: 30
};
regularPoint.x = 15; // 가능: 'Point'의 속성은 수정 가능
console.log(regularPoint); // 출력: { x: 15, y: 10 }
// readonlyPoint.x = 25; // 오류: 읽기 전용 속성은 수정 불가
console.log(readonlyPoint); // 출력: { x: 20, y: 30 }
function movePoint(p: Point, dx: number, dy: number): Point {
return { x: p.x + dx, y: p.y + dy };
}
const movedRegularPoint = movePoint(regularPoint, 3, 4);
console.log(movedRegularPoint); // 출력: { x: 18, y: 14 }
// const movedReadonlyPoint = movePoint(readonlyPoint, 3, 4); // 오류: 'ReadonlyPoint'는 'Point'로 변환 불가
Readonly 타입을 사용하면 객체의 모든 속성이 수정 불가능해져 immutability(불변성)를 강제합니다.
ReadonlyPoint는 읽기 전용 속성이기 때문에 수정할 수 없으며, movePoint 함수와 같이 매개변수를 필요로 하는 함수에는 사용할 수 없다.
TypeScript의 데코레이터는 클래스, 메서드, 속성, 매개변수에 메타데이터를 추가하거나 동작을 수정 및 확장할 수 있는 강력한 기능이다.
데코레이터는 고차 함수로, 클래스 정의, 메서드 정의, 접근자 정의, 속성 정의, 매개변수 정의를 관찰, 수정 또는 교체하는 데 사용할 수 있다.
function LogClass(target: Function) {
console.log(`Class ${target.name} was defined.`);
}
@LogClass
class MyClass {
constructor() {}
}
// 출력: Class MyClass was defined.
이 데코레이터는 데코레이터가 적용된 클래스가 정의될 때 클래스 이름을 로그로 출력한다.
function LogMethod(target: any, key: string, descriptor: PropertyDescriptor) {
console.log(`Method ${key} was called.`);
}
class MyClass {
@LogMethod
myMethod() {
console.log("Inside myMethod.");
}
}
const instance = new MyClass();
instance.myMethod();
// 출력:
// Method myMethod was called.
// Inside myMethod.
LogMethod는 메서드 데코레이터로, 메서드가 호출될 때 메서드 이름을 로그로 출력한다.
function DefaultValue(value: any) {
return (target: any, key: string) => {
target[key] = value;
};
}
class MyClass {
@DefaultValue(42)
myProperty: number;
}
const instance = new MyClass();
console.log(instance.myProperty);
// 출력: 42
DefaultValue 속성 데코레이터는 속성에 기본값을 설정한다.
function LogParameter(target: any, key: string, parameterIndex: number) {
console.log(`Parameter at index ${parameterIndex} of method ${key} was called.`);
}
class MyClass {
myMethod(@LogParameter value: number) {
console.log(`Inside myMethod with value ${value}.`);
}
}
const instance = new MyClass();
instance.myMethod(5);
// 출력:
// Parameter at index 0 of method myMethod was called.
// Inside myMethod with value 5.
LogParameter는 매개변수 데코레이터로, 메서드가 호출될 때 해당 매개변수의 인덱스와 메서드 이름을 로그로 출력합니다.
데코레이터는 클래스, 메서드, 속성, 매개변수의 동작을 수정하거나 확장할 수 있는 TypeScript의 고급 기능이다.
@ 문법을 통해 적용되며, 코드의 동작을 유연하게 제어하는 데 유용하다.
TypeScript에서 네임스페이스는 관련된 코드를 조직화하고 그룹화하는 방법이다.
이를 통해 이름 충돌을 방지하고 모듈화를 촉진할 수 있다. 네임스페이스는 클래스, 인터페이스, 함수, 변수 및 다른 네임스페이스를 포함할 수 있다.
namespace 키워드를 사용하여 네임스페이스를 정의. export 키워드를 사용.namespace MyNamespace {
export class MyClass {
constructor(public value: number) {}
displayValue() {
console.log(`The value is: ${this.value}`);
}
}
}
네임스페이스 내부 코드를 사용할 때, 두 가지 방법이 있다.
// 전체 경로 사용
const instance1 = new MyNamespace.MyClass(5);
instance1.displayValue(); // 출력: The value is: 5
// 네임스페이스 import 사용
import MyClass = MyNamespace.MyClass;
const instance2 = new MyClass(10);
instance2.displayValue(); // 출력: The value is: 10
namespace OuterNamespace {
export namespace InnerNamespace {
export class MyClass {
constructor(public value: number) {}
displayValue() {
console.log(`The value is: ${this.value}`);
}
}
}
}
// 전체 경로 사용
const instance = new OuterNamespace.InnerNamespace.MyClass(15);
instance.displayValue(); // 출력: The value is: 15
네임스페이스는 관련된 코드를 그룹화하고 이름 충돌을 방지하는 데 사용된다.
export 키워드를 사용하면 네임스페이스 밖에서 코드를 사용할 수 있다.
네임스페이스는 전체 경로를 사용하거나 import를 통해 사용이 가능하다.
중첩 네임스페이스는 코드 구조를 계층적으로 만들어 더 체계적인 코드를 작성할 수 있게 해준다.
믹스인은 TypeScript에서 여러 작은 부분으로부터 클래스를 구성하는 방법다.
믹스인 클래스를 사용하면 클래스 간에 동작을 재사용하고 공유할 수 있으며, 이를 통해 모듈성 및 코드 재사용성을 높일 수 있다.
class TimestampMixin<TBase extends new (...args: any[]) => any>(Base: TBase) {
constructor(...args: any[]) {
super(...args);
}
getTimestamp() {
return new Date();
}
}
extends 키워드를 사용해 적용.class MyBaseClass {
constructor(public value: number) {}
displayValue() {
console.log(`The value is: ${this.value}`);
}
}
class MyMixedClass extends TimestampMixin(MyBaseClass) {
constructor(value: number) {
super(value);
}
}
const instance = new MyMixedClass(42);
instance.displayValue(); // Output: The value is: 42
const timestamp = instance.getTimestamp();
console.log(`The timestamp is: ${timestamp}`); // Output: The timestamp is: [현재 날짜 및 시간]
이 클래스는 MyBaseClass의 displayValue 메서드와 TimestampMixin의 getTimestamp 메서드를 모두 포함한다.
믹스인은 여러 클래스를 조합하여 하나의 클래스로 만들 수 있는 기능으로, 코드 재사용성과 모듈성을 높인다.
믹스인 클래스는 제네릭을 사용해 다양한 클래스와 결합할 수 있으며, 결합된 클래스는 믹스인의 기능을 상속받는다.
TypeScript에서 변수 또는 매개변수의 타입을 좁히는 방법이다.
이를 통해 서로 다른 타입을 구별하고, 특정 타입에 맞는 속성이나 메서드를 사용할 수 있어 타입 안정성을 높이고 런타임 오류를 줄일 수 있다.
function isString(value: any): value is string {
return typeof value === "string";
}
함수는 value is string이라는 타입 서술자를 반환하여 value 변수가 문자열 타입으로 좁혀지게 한다.
function processValue(value: string | number) {
if (isString(value)) {
console.log(`The length of the string is: ${value.length}`);
} else {
console.log(`The square of the number is: ${value * value}`);
}
}
processValue("hello"); // Output: The length of the string is: 5
processValue(42); // Output: The square of the number is: 1764
isString 타입 가드가 값을 확인하여, 타입에 맞는 코드 블록이 실행되고, 각 타입에 맞는 속성이나 메서드를 사용할 수 있게 한다.
Type Guards는 코드 내에서 변수의 타입을 안전하게 좁혀주는 기능이다.
타입 가드 함수는 매개변수가 특정 타입임을 확인하고, 타입에 맞는 속성이나 메서드를 사용할 수 있도록 해준다.
조건문에서 타입 가드를 사용하여 런타임 오류 없이 타입에 맞는 코드를 실행할 수 있다.