✨ 제네릭 타입 별칭
type Nullish<T> = T | null | undefined;
제네릭 판별된 유니언
예시로 데이터의 성공적인 결과 또는 오류로 인한 실패를 나타내는 제네릭 결과 타입을 만들 수 있습니다.
type Result<Data> = FailureResult | SuccessResult<Data>
interface FailureResult {
error: Error;
ok: false;
}
interface SuccessResult<Data> {
data: Data;
ok: true
}
function handleResult(result: Result<string>) {
if(result.ok){
console.log(result.data)
}
if(!result.ok){
console.log(result.error)
}
}
✨ 제네릭 제한자
제네릭 기본값
interface Quote<T = string> {
value: T
}
const quote1: Quote<number> = {value: 123}
const quote2: Quote = {value: "ABC"}
interface KeyValuePair<Key, Value = Key> {
key: Key;
value: Value;
}
const pair1: KeyValuePair<string, number> = {
key: "ABC",
value: 123
}
const pair2: KeyValuePair<string> = {
key: "ABC",
value: "123"
}
함수의 인스턴스와 마찬가지로 기본값을 설정한 제네릭은 제일 마지막에 와야합니다.
interface KeyValuePair<Value = string, Key> {
// ERROR Required type parameters may not follow optional type parameters.
key: Key;
value: Value;
}
✨ 제한된 제네릭 타입
interface WithLength {
length: number
}
function logLength<T extends WithLength>(value: T) {
console.log(value.length)
}
keyof와 제한된 타입 매개변수
function get<T, Key extends keyof T>(obj: T, key: Key) {
return obj[key]
}
const person = {
name: "ABCD",
age: 12
}
const res = get(person, "name") // const res: string
// 정확히 string 타입으로 유추한다.
만약 위와 달리, 다음과 같이 사용한다면, 모든 속성에 대한 유니언 값을 나타냅니다.
function get<T>(obj: T, key: keyof T) {
return obj[key]
}
const person = {
name: "ABCD",
age: 20
}
const res = get(person, "name") // const res: string | number
// string 타입으로 내로잉하지 못한다.
✨ Promise
대략적인 Promise의 타입 모습입니다.
type Resolve<Value> = (value: Value) => void;
type Reject = (reason: unknown) => void;
type Executor<Value> = (resolve: Resolve<Value>, reject: Reject) => void;
class PromiseLike<T> {
constructor(executor: Executor<T>) {
// ...
}
}
명시적인 타입 인수가 없다면 기본적으로 unknown으로 간주합니다.
const resolve1 = new Promise((resolve) => {
setTimeout(() => resolve("Done"), 1000)
}) // const resolve1: Promise<unknown>
const resolve2 = new Promise<string>((resolve) => {
setTimeout(() => resolve("Done"), 1000)
}) // const resolve2: Promise<string>
.then 메서드는 반환되는 Promise의 resolve된 값을 나타내는 새로운 타입 매개변수를 받습니다.
const resolve = new Promise<string>((resolve) => {
setTimeout(() => resolve("Done"), 1000)
}) // const resolve: Promise<string>
const res = resolve.then(text => text.length)
// const res: Promise<number>
async 함수
async 함수는 Promise를 반환합니다. Thenable(.then() 메서드가 있는 객체)가 아닌 경우, Promise,resolve가 호출된 것 처럼 래핑(Wrapping)됩니다.
async 함수는 항상 Promise 타입을 반환합니다.
async function lengthAfterSecond(text: string) {
await new Promise((resolve) => setTimeout(resolve, 1000))
return text.length
} // function lengthAfterSecond(text: string): Promise<number>
async function lengthImmediately(text: string) {
return text.length
} // function lengthImmediately(text: string): Promise<number>
async function giveString() {
return "String!"
} // function giveString(): Promise<string>
✨ 제네릭 올바르게 사용하기
제네릭 황금률
함수에 타입 매개변수가 필요한지 여부를 판단할 수 있는 간단한 방법은, 타입 매개변수가 최소 두 번 이상 사용되었는지 확인하는 것입니다.
function logValue<Input extends string>(input: Input){
console.log(input)
}
위 Input 타입을 선언하는 것은 쓸모없습니다. 다음과 같이 수정합시다.
function logValue(input: string){
console.log(input)
}
제네릭 명명 규칙
기본적으로 첫 번째 타입 인수는 T를 사용하고, 다음으로 U,V 등을 사용합니다.
타입 인수가 어떻게 사용되어야 할지, 맥락과 정보가 알려진 경우라면, 해당 용어의 첫 글자를 사용하는 것으로 확장합니다.
제네릭의 의도가 명확하지 않다면, 타입 이름을 사용하세요.
function labelBoxFn1<L,V>(l: L, v:V) { } // 불분명합니다.
function labelBoxFn2<Lable,Value>(lable: Lable, v:Value) { } // OK
반응형