You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
1. 제너릭을 사용하는 이유(any와 비교)
2. 기본 제너릭 타입 정의(function, interface, type, class)
3. 제너릭 제약조건, 타입매개변수 사용
4. 제너릭 유틸리티 타입
4-1. Partial<T> (<-> 용도에 따라 반대개념Required)
4-2. Required<T>
4-3. Readonly<T>
4-4. Record<K,T>
4-5. Pick<T,K> (<-> Omit) 인터페이스나 객체에서 제와하거나 해당하는 타입 생성
4-6. Omit<T,K>
4-7. Exclude<T,U> (<-> Extract) 유니언타입에서 주로 사용
4-8. Extract<T,U>
4-9. NonNullable<T>
4-10. Parameters<T>
4-11. ReturnType<T>
4-12. ConstructorParameters<T>
4-13. InstanceType<T>
4-14. ThisParameterType
4-15. OmitThisParameter
4-16. ThisType<T>
1. 제너릭을 사용하는 이유(any와 비교)
타입스크립트로 한가지 타입이 아니라 여러가지 타입을 받아야할 때 any를 사용할 경우 타입을 잃어버려요
// 원래 소스constfunc=(message: string): string=>{returnmessage}console.log(func('1'));
// 여러가지 타입을 받아야 할 경우 -> any -> 타입 잃어버림constfuncAny=(message: any): any=>{return(message);}// 뭘 넣든 호출시그니처 확인 시 any타입console.log(funcAny(1));// const funcAny: (message: any) => any
// T(타입변수) 사용해서 제네릭 타입 표현constfuncType=<T>(message: T):T=>{returnmessage;};console.log(funcType(1));// const funcType: <1>(message: 1) => 1 (타입 매개변수를 생략: T는 1타입이 됨)// 복잡한 타입유추 시에는 아래처럼 타입 명시 필요 (타입 매개변수를 명시적으로 지정)console.log(funcType<number>(1));// const funcType: <number>(message: number) => number
위의 예시는 설명을 위해 작성한 것일 뿐,
실제로 사용시에는 역추적 가능한 타입에 대해서는 굳이 타입명시를 하지 않습니다.
// ex.
const [data, setData] = useState<number>(0); // 클린코드 관점에서 별로에요
const [data, setData] = useState(0); // 역추적이 가능하기 때문에 굳이 명시하지 않아요
초기값이 없는 state에도 굳이 undefined를 명시하지않아요
const [data, setData] = useState();
3번 이상 재사용 할게 아니면 굳이 제네릭으로 타입추론 할 필요가 없다고 생각.
(제네릭을 잘못 사용하면 온통 제네릭 밭이 되어버려요)
// 4-4. Record<T,K>// T를 속성의 키로, K를 벨류로 사용// 키인 T에 매칭하는 K가 구체적일 때 사용 (키값을 아는 경우에 주로 사용)typeTRecord<Textendskeyofany,K>={// 반복되니까 [P in T] mapped 타입으로 표현[PinT]: K}typeUser={id: number;name: string;}// 구현한거 사용constuserArray: TRecord<number,User>={0: {id: 0,name: 'gayoung'},1: {id: 1,name: 'kong'}}// 유틸리티 함수 사용constuserArray2: Record<number,User>={0: {id: 0,name: 'gayoung'},1: {id: 1,name: 'kong'}}// 인덱스 시그니처 사용 typeTUser={[id: number]: User;}constuserArray3: TUser={0: {id: 0,name: 'gayoung'},1: {id: 1,name: 'kong'}}// 인덱스 시그니처 객체의 이름과 속성을 미리 알 필요 없고 추가도 가능해서 훨신 유연함typeTPerson={[keyinstring] : string|number;}constperson1: TPerson={name: 'gayoung',id: 27}person1.email='[email protected]';
// 4-5. Pick<T,K>// T에서 K속성만 가지는 타입 생성// K를 T의 keyof으로 만들어서 반복문으로 mapped하게 만들기typeTPick<T,KextendskeyofT>={[PinK]: T[K]}interfaceIResponse<T>{code: number;message: string;data: T}typec=TPick<IResponse<{flag: boolean}>,'data'>;typeb=Pick<IResponse<{flag: boolean}>,'data'>;// { data: {flag: boolean } }
// 4-6. Omit<T,K>// T에서 K 제외한 타입 생성typeTOmit<T,K>=Pick<T,Exclude<keyofT,K>>;interfaceIResponse<T>{code: number;message: string;data: T}typec=TOmit<IResponse<{flag: boolean}>,'data'>;typeb=Omit<IResponse<{flag: boolean}>,'data'>;// {code: number; message: string;}
// 4-7. Exclude<T,U>// T에서 유니언 타입 U에 해당하는 속성 제외typeTExclude<T,U>=TextendsU ? never : T;interfaceIResponse<T>{code: number;message: string;data: T}typec=TExclude<IResponse<{flag: boolean}>,{code: number}|{message: string}>;// nevertypeb=Exclude<IResponse<{flag: boolean}>,{code: number}|{message: string}>;// never// type Exclude<T, U> = T extends U ? never : T U가 T에 확장형이면 없애고, 아닌것만 리턴typeT0=Exclude<"a"|"b"|"c","a">;// "b" | "c"typeT1=Exclude<"a"|"b"|"c","a"|"b">;// "c"
Omit과 Exclude는 동일한 결과를 반환하지만, 과정상 Omit은 특정 속성을 제거한 타입 반환
Exclude는 특정 Union Type을 제외한 나머지 Union Type을 제거함
=> Omit은 객체나 인터페이스에서, Exclude는 유니언에서
=> Pick은 객체나 인터페이스에서, Extract은 유니언에서
// 4-8. Extract<T,U>// T에서 U에 해당하는 속성들만 추출typeExtract<T,U>=TextendsU ? T : never;typeT0=Extract<"a"|"b"|"c","a">;// "a"typeT1=Extract<"a"|"b"|"c","a"|"b"|"F">;// "a" | "b"
// 4-9. NonNullable<T>// null과 undefined 제외타입 생성 ({}와 같은의미)typeTNonNullable<T>=Textends{} ? T : never;// type NonNullable<T> = T & {} 이렇게 쓴다 typeb=TNonNullable<string|number|undefined>;// string | numbertypea=NonNullable<string|number|undefined>;// string | number
// 함수타입 T 매개변수와 타입을 튜플로 추출// 4-10. Parameters<T>// 함수타입 T의 리턴 추출// 4-11. ReturnType<T>constfunc=(a: number,b: number) :string=>{returna.toString()+b.toString();}// 직접 구현// infer로 동적으로 타입 유추 가능typeTParameters<Textends(...args: any)=>any>=Textends(...args: infer P)=>any ? P : never;typea0=TParameters<typeoffunc>;// {a: number, b:number}typeTResponse<Textends(...args: any)=>any>=Textends(...args: infer P)=> infer R ? R : never;typeb0=TResponse<typeoffunc>;// returnType string;// 타입스크립트에서 제공하는 함수 Parameters,ReturnTypetypea=Parameters<typeoffunc>;// {a: number, b:number}typeb=ReturnType<typeoffunc>;// returnType string;
// 4-12. ConstructorParameters<T>// 클래스타입 생성자 T의 매개변수들을 튜플로 추출classperson{name : string;age: number;constructor(name:string,age:number){this.name=name;this.age=age;}}typeTConstructorParameters<Textendsabstractnew(...args: any)=>any>=Textendsabstractnew(...args: infer P)=>any ? P : never;// 직접구현typea0=TConstructorParameters<typeofperson>;// 타입스크립트에서 제공하는 함수 ConstructorParameterstypea=ConstructorParameters<typeofperson>;// [name: string, age: number]