조건에 따라 다른 type이 되는 컨디셔널 타입 존재
타입검사를 위해도 많이 사용함. (함수로도 사용이 가능할듯요)
type A1 = string;
type B1 = A1 extends string? number : boolean;
// type은 값처럼 쓸수없다. 기본삼항연산자 대신 extends 키워드를 사용해야함.
type Result = "hi" extends string? true : false;
}
function isType<T>(value: any, targetType: { new (): T }): value is T {
return value instanceof targetType;
}
const result1 = isType(42, Number); // true
const result2 = isType("Hello, World!", String); // true
컨디셔널 타입은 never와 함께 사용가능함. => TS에서 특정타입인 속성을 제거하는 타입으로 활용가능함.
타입스크립트에서 타입은 불변임. => 객체 타입에서 한 속성을을 제거하고 싶으면 기존타입을 변환하고 필터링해 새로운 타입을 만들어야 한다.
매핑된 타입의 키를 never 조건부로 다시 매핑하면 해당키가 필터링됨.
유틸리티 타입 Omit도 존재함. => Omit은 특정키를 기준으로 생략하여 타입을 필터링해줌.
type OmitByType<O,T> = {
[Key in keyof O as O[Key] extends T ? never : Key] : O[Key];
}
// O의 key type들중 T type을 extend한다면, never이고 아니면 Key이다.
// 키가 never라면 해당 속성은 제거됨.
type Result = OmitByType<"type을 생략하고 싶은 interface나 type", boolean>
// T로 넣은 타입들중 booelan 속성을 모두 제거함.
interface Todo {
title: string
description: string
completed: boolean
createdAt: number
}
// description을 제외
type TodoPreview = Omit<Todo, 'description'>
언제사용하는가? union type으로 어떤 타입이 들어올지 특정 할 수 없을때사용
Start type은 string과 number의 union인데, 만약 Start가 number라면 string을 extends 하지 못하기 때문에 never로 판별됨.
이럴때 제너릭을 사용해서 각각을 분배법칙처럼 분리가능함.
Result<string|number> => Result | Result 로 판단, Key가 string이라면 string,
type Start = string | number; // 여기서 string[] 타입을 얻고 싶을때
type Result = Start extends string? Start[] : never;
type Result<Key> = Key extends string ? Key[] : never; //Key 들이 string을 extend하면 ? string[]로 변환
let n : Result<Start> = ["hi"]; // string[]
type IsString1<T> = T extends string? true:false;
type IsString2<T> = [T] extends [string]? true:false;
type Result1 = IsString1<"hi"|3> // boolean
type Result2 = IsString2<"hi"|3> // false
function example4(...args : [a:number, b:string, c:boolean]){
}
type stringNum = string|number
function add(x:stringNum, y:stringNum):stringNum{
if(typeof x === "number" && typeof y === "number"){
return x+y;
}
...//이렇게 4가지경우에 대해 모두 타입가드를 해줘야함.
}
function switchIt(input) {
if (typeof input === 'string') return Number(input)
else return String(input)
}
function switchIt<T extends string | number>(
input: T,
): T extends string ? number : string {
if (typeof input === 'string') {
return Number(input) as string & number
} else {
return String(input) as string & number
}
}
const num = switchIt('1') // has type number ✅
const str = switchIt(1) // has type string ✅
왜 굳이 오버로딩을 할까? 그냥 하나 선언한다음에 타입가드로 처리하면 안되는걸까?
제너릭 방식으로 처리한 코드 예시
(ref : https://yceffort.kr/2022/03/polymorphic-function-in-typescript#%ED%95%A8%EC%88%98-%EC%98%A4%EB%B2%84%EB%A1%9C%EB%93%9C)
함수 오버로딩 사용 => 즉 union type의 파라미터를 여러개 받을 때 가독성 항샹을 위해 사용함.
파라미터의 타입을 특정 할 수 없음(조건부 연산자와 제너릭으로 타입을 특정해줘야함.)
function switchIt_overloaded(input: string): number
function switchIt_overloaded(input: number): string
function switchIt_overloaded(input: number | string): number | string {
if (typeof input === 'string') {
return Number(input)
} else {
return String(input)
}
}
interface Add{
(x:number, y:number):number;
(x:string, y:string):string;
}
const add3:Add = (x:any,y:any) => x+y;
type Add1 = (x:number, y:number) => number;
type Add2 = (x:string, y:string)=>string;
type AddFn = Add1 & Add2;
const Add4:AddFn = (x:any,y:any) => x+y
class Person{
name:string;
married : boolean;
age: number;
constructor(name:string, age:number, married:boolean){
this.name =name;
this.age = age;
this.married =married;
}
}
class Person2{
name;
married;
age;
constructor(name:string, age:number, married:boolean){
this.name =name;
this.age = age;
this.married =married;
}
} //생략해도 타입추론함
const newppl = new Person2("s",12,true);
interface IPerson{
name:string;
married:boolean;
age:number
}
class Person2 implements IPerson{
name;
married;
age;
constructor(name:string, age:number, married:boolean){
this.name =name;
this.age = age;
this.married =married;
}
}
const person1:Person2 = new Person2("s",12,true); //인스턴스타입
const personClass:typeof Person2 = Person //클래스타입
enum Level{
NOVICE,
INTERMIDIATE,
ADVANCED,
MASTER,
}
//JS에서 아래와 같이 컴파일됨.
let Level;
(function (Level) {
Level[Level["NOVICE"] = 0] = "NOVICE";
Level[Level["INTERMIDIATE"] = 1] = "INTERMIDIATE";
Level[Level["ADVANCED"] = 2] = "ADVANCED";
Level[Level["MASTER"] = 3] = "MASTER";
})(Level || (Level = {}));