때로는 코드에서 호출되는 방식에 따라 다양한 타입으로 작동하도록 의도할 수 있습니다.
TypeScript에서 함수와 같은 구조체는 제네릭 타입 매개변수를 원하는 수만큼 선언할 수 있습니다.
제네릭 구조체에서는 <
, >
를 사용해 someFunction<T>
또는 SomeInterface<T>
처럼 제네릭을 선언합니다.
<
, >
)로 묶인 타입 매개변수에 별칭을 배치해 함수를 제네릭으로 만듭니다.function identity<T>(input: T) {
return input;
}
// 타입 : 'me'
const stringy = identity('me');
// 타입 : 123
const numeric = identity(123);
(
앞에 위치합니다.const identity = <T>(input: T) => input;
// 타입 : 123
identity(123);
function logWrapper<Input>(callback: (input: Input) => void) {
return (input: Input) => {
console.log('Input:', input);
callback(input);
};
}
// 타입 : (input: string) => void
logWrapper((input: string) => {
console.log(input.length);
});
// 타입 : (input: unknown) => void
logWrapper((input) => {
// Error: Property 'length' does not exist on 'unknown'
console.log(input.length);
});
이름<Type>
와 같이 지정합니다.// 타입 : (input: string) => void
logWrapper<string>((input) => {
console.log(input.length);
});
// Error: Argument of type '(input: boolean) => void' is not assignable
// to paramter of type '(input: string => void'
// Type of paramters 'input' and 'input' are incompatible
// Type 'string' is not assignable to type 'boolean'
logWrapper<string>((input: boolean) => {
console.log(input.length);
});
function makeTuple<First, Second>(first: First, second: Second) {
return [first, second] as const;
}
// 타입 : readonly [boolean, string]
let tuple = makeTuple(true, 'abc');
function makePair<Key, Value>(key: Key, value: Value) {
return {key, value};
}
// Ok, 타입 인수가 둘 다 제공되지 않음
// 타입 : {key: string, value: number }
makePair('abc', 123)
// Ok, 2개의 타입 인수가 제공됨
// 타입 : {key: string, value: number }
makePair<string, number>('abc', 123);
// 타입 : {key: 'abc', value: 123 }
makePair<'abc', 123>('abc', 123);
// Error: Expected 2 type arguments, but got 1
makePair<string>('abc', 123);
<
와 >
사이에 선언된 임의의 수의 타입 매개변수를 갖습니다.interface Box<T> {
inside: T;
}
let stringyBox: Box<string> = {
inside: 'abc',
};
let numberBox: Box<number> = {
inside: 123,
};
let incorrectBox: Box<number> = {
// Error: Type 'boolean' is not assignable to type 'number'
inside: false,
};
interface Array<T> {
// pop : 배열에서 마지막 요소를 제거하고 그 요소를 반환하는 메서드, 빈 배열일 경우 undefined를 반환함
pop(): T | undefined;
// push: 배열 끝에 새로운 요소를 추가하고 배열의 길이를 반환하는 메서드
// @param items 배열에 추가된 새로운 요소
push(...items: T[]): number;
}
interface LinkedNode<Value> {
next?: LinkedNode<Value>;
value: Value;
}
function getLast<Value>(node: LinkedNode<Value>): Value {
return node.next ? getLast(node.next) : node.value;
}
// 유추된 Value 타입 인수 : Date
let lastDate = getLast({
value: new Date('09-13-1993')
})
// 유추된 Value 타입 인수 : string
let lastFruit = getLast({
next: {
value: 'banana'
},
value: 'apple'
});
let lastMismatch = getLast({
next: {
value: 123
},
// Error: Type 'boolean' is not assignable to type 'number'
value: false
});
interface CrateLike<T> {
contents: T;
}
// Error: Generic type 'CrateLike<T>' requires 1 type argument(s)
let missingGeneric: CrateLike = {
inside: '??'
}
class Secret<Key, Value> {
key: Key;
value: Value;
constructor(key: Key, value: Value) {
this.key = Key;
this.value = Value;
}
getValue(key: Key): Value | undefined {
reeturn this.key === key ? this.value : undefined;
}
}
// 타입 : Secret<string, number>
const storage = new Secret(12345, 'luggage');
// 타입 : string | undefined
storage.getValue(1987);
class CurriedCallback<Input> {
#callback: (input: Input) => void;
constructor(callback: (input: Input) => void) {
this.#callback = (input: Input) => {
console.log('Input:', input);
callback(input);
};
}
call(input: Input) {
this.#callback(input);
}
}
// 타입 : CurriedCallback<string>
new CurriedCallback((input: string) => {
console.log(input.length);
});
// 타입 : CurriedCallback<unknown>
new CurriedCallback((input) => {
// Error: Property 'length' does not exist on tyep 'unknown'
console.log(input.length);
});
// 타입 : CurriedCallback<string>
new CurriedCallback<string>((input) => {
console.log(input.length);
});
new CurriedCallback<string>((input: boolean) => {
// Error: Argument of type '(input: boolean) => void' is not assinable
// to parameter of type '(input: string) => void'
// Type of parameters 'input' and 'input' are incompatible
// Type 'string' is not assignable to type 'boolean'
console.log(input.length);
});
class Quote<T> {
lines: T;
constructor(lines: T) {
this.lines = lines;
}
}
class SpokenQuote extends Quote<string[]> {
speak() {
console.log(this.lines.join('\n'));
}
}
// 타입 : string
new Quote('The only real fear is the failure to try.').lines;
// 타입 : number[]
new Quote([4, 5, 6, 7, 8, 9]).lines;
// 타입 : string[]
new SpokenQuote([
'Greed is so destructive',
'It destroys everything'
]).lines;
// Error: Argument of type 'number' is not assignable to parameter of type 'string'
new SpokenQuote([1, 2, 3, 4, 5]).lines;
interface ActingCredit<Role> {
role: Role;
}
class MoviePart implements ActingCredit<string> {
role: string;
speaking: boolean;
constructor(role: string, speaking: boolean) {
this.role = role;
this.speaking = speaking;
}
}
const part = new MoviePart('Miranda Priestly', true);
// 타입 : string
part.role;
class IncorrectExtension implements ActingCredit<string> {
// Error: Property 'role' in type 'IncorrectExtension' is not assignable to the same property in base type 'ActingCredit<string>'.
// Type 'boolean' is not assignable to type 'string'.
role: boolean;
}
class CreatePairFactory<Key> {
key: Key;
constructor(key: Key) {
this.key = key;
}
createPair<Value>(value: Value) {
return {key: this.key, value };
}
}
// 타입 : createPairFactory<string>
const factory = new CreatePairFactory('role');
// 타입 : { key: string, value: number }
const numberPair = factory.createPair(10);
// 타입 : { key: string, value: string }
const stringPair = factory.createPair('Sophie');
class BothLogger<OnInstance> {
instanceLog(value: OnInstance) {
console.log(value);
return value;
}
static staticLog<OnStatic>(value: OnStatic) {
// Error: Static members cannot reference class types arguments
let fromInstance: OnInstance;
console.log(value);
return value;
}
}
const logger = new BothLogger<number[]>;
// 타입 : number[]
logger.instanceLog([1, 2, 3]);
// 유추된 OnStatic 타입 인수 : boolean[]
BothLogger.staticLog([false, true]);
// 유추된 OnStatic 타입 인수 : string[]
BothLogger.staticLog(['abc', 'def']);
type Nullish<T> = T | null | undefined;
type CreatesValue<Input, Output> = (input: Input) => Output;
// 타입 : (input: string) => number
let creator: CreatesValue<string, number>;
// Ok
creator = text => text.length;
// Error: Type 'string' is not assignable to type 'number'
creator = text => text.toUpperCase()
type Result<Data> = FailureResult | SuccessfulResult<Data>;
interface FailureResult {
error: Error;
successed: false;
}
interface SuccessfulResult<Data> {
data: Data;
successed: true;
}
function handleResult(result: Result<string>) {
if (result.successed) {
// result : SuccessfulResult<string>
console.log(`We did it! ${result.data}`);
} else {
// result : FailureResult
console.log(`Awww....${result.error}`);
}
// Error: Property 'data' does not exist on type 'Result<string>'.
// Property 'data' does not exist on type 'FailureResult'
result.data;
}
=
과 기본 타입을 배치해 타입 인수를 명시적으로 제공할 수 있습니다.interface Quote<T = string> {
value: T;
}
let explicit: Quote<number> = { value: 123 };
let implicit: Qoute = { value: 'Be yourself. The world worships the original.' };
// Error: Type 'number' is not assignable to type 'string'
let mismatch: Qoute = { value: 123 };
interface KeyValuePair<Key, Value = Key> {
key: Key;
value: Value;
}
// 타입 : KeyValuePair<string, number>
let allExplicit: KeyValuePair<string, number> = {
key: 'rating',
value: 10,
};
// 타입 : KeyValuePair<string>
let allExplicit: KeyValuePair<string> = {
key: 'rating',
value: 'ten',
};
// Error: Generic type 'KeyValuePair<Key, Value>' requires between 1 and 2 type arguments.
let firstMissing: KeyValuePair = {
key: 'rating',
value: 10
};
// Ok
function inTheEnd<First, Second, Third = number, Fourth = string>() {}
// Error: Required type parameters may not follow optional type parameters.
function inTheMiddle<First, Second = boolean, Third = number, Forth>() {}
extends
키워드를 배치하고 그 뒤에 이를 제한할 타입을 배치합니다.interface WithLength {
length: number;
}
function logWithLength<T extends WithLength>(input: T) {
console.log(`length: ${input.length}`);
return input;
}
// 타입 : string
logWithLength('No one can figure out your worth but you');
// 타입 : boolean[]
logWithLength([false, true]);
// 타입 : { length: 123 }
logWithLength({ length: 123 });
// Error: Argument of type 'Date' is not assignable to parameter of type 'WithLength'.
// Property 'length' is missing in type 'Date' but required in type 'WithLength'.
logWithLength(new Date());
keyof
연산자는 제한된 타입 매개변수와도 잘 작동합니다.extends
와 keyof
를 함께 사용하면 타입 매개변수를 이전 타입 매개변수의 키로 제한할 수 있습니다.function get<T, Key extends keyof T>(container: T, key: Key) {
return container[key];
}
const roles = {
favorite: 'Fargo',
others: ['Almost Famous', 'Burn After Reading', 'Nomadland'],
};
// 타입 : string
const favorite = get(roles, 'favorite');
// 타입 : string[]
const others = get(roles, 'others');
// Error: Arguments of type 'extras' is not assignable to parameter "'favorite' | 'others'"
const missing = get(roles, 'extras');
function get<T>(container: T, key: keyof T) {
return container[key];
}
const roles = {
favorite: 'Fargo',
others: ['Almost Famous', 'Burn After Reading', 'Nomadland'],
};
// 타입 : string | string[]
const favorite = get(roles, 'favorite');
JavaScript의 Promise는 네트 워크 요청과 같이 요청 이후 결과를 받기까지 대기가 필요한 것을 나타냅니다.
Promise는 TypeScript 타입 시스템에서 최종적으로 resolve된 값을 나타내는 단일 타입 매개변수를 가진 Promise 클래스로 표현됩니다.
class PromiseLike<Value> {
constructor(
executor: (
resolve: (value: Value) => void,
reject: (reason: unknown) => void,
) => void
){ /* ... */}
}
// 타입 : Promise<unknown>
const resolvesUnknown = new Promise((resolve) => {
setTimeout(() => resolve('Done!', 1000));
});
// 타입 : Promise<string>
const resolvesString = new Promise<string>((resolve) => {
setTimeout(() => resolve('Done!', 1000));
});
// 타입 : Promise<string>
const textEventually = new Promise<string>((resolve) => {
setTimeout(() => resolve('Done!', 1000));
});
// 타입 : Promise<number>
const lengthEventually = textEventually.then((text) => text.length);
async
키워드를 사용해 선언한 모든 함수는 Promise를 반환합니다. JavaScript에서 async 함수에 따라서 반환된 값이 Thenable(.then() 메서드가 있는 객체, 거의 항상 Promise)이 아닌 경우, Promise.resolve가 호출된 것처럼 Promise로 래핑됩니다.async function givesPromiseForString(): Promise<string> {
return 'Done!';
}
// Error: The return type of an async function or method must be the global Promise<T> type
async function giveString() : string {
return 'Done!';
}
// Bad
function logInput<Input extends string>(input: Input) {
console.log('Hi', input);
}
// Good
function logInput(input: string) {
console.log('Hi', input);
}
// Bad
function labelBox<L, V>(l: L, v: V){ /* ... */ }
// Good
function labelBox<Label, Value>(l: Label, v: Value){ /* ... */ }