해당 글의 조회수가 꽤 나와서 내용을 보강하였습니다.
참고로 끼워넣은 게시물들도 읽어주세요...
0. typeof 연산자
해당 연산자는 자바스크립트에도 존재하지만 타입스크립트 타입, 인터페이스 문법에도 확장하여 사용할 수 있다.
기본적으로 값은 "값 공간"에 존재한다.
값 공간에 있는 값에 typeof 연산자를 적용하여 값 공간의 값을 타입 공간에서 타입으로 이용할 수 있다.
타입 공간과 타입 계층에 대해 더 잘 알고 싶으면 아래 글을 참고한다.
https://itchallenger.tistory.com/447?category=1091150
let s = "hello";
let n: typeof s; // let n : string
const add1 = (x:number)=>x+1
type MyAdd1Type = typeof add1 // (x:number)=>number
// 아래는 오류가 발생한다. 이유를 생각해보자.
function f() {
return { x: 10, y: 3 };
}
type P = ReturnType<f>; // 'f' refers to a value, but is being used as a type here. Did you mean 'typeof f'?
// <>안에는 타입이 들어가야 하기 때문이다.
// 해당 문법은 정상동작한다.
function f() {
return { x: 10, y: 3 };
}
type P = ReturnType<typeof f>;
type P = {
x: number;
y: number;
}
1. 들어가며 : 타입 대수
- 교집합 연산자는 합집합 연산에 분배할 수 있습니다
- A & (B | C) === (A & B) | (A & C). A의 교집합(&) 연산을 B | C에 분배했습니다.
- 합집합 연산자도 교집합 연산에 분배할 수 있습니다.
- A | (B & C) === (A | B) & (A | C).
이 내용이 중요한 이유는, 모든 타입을 단일 필드 오브젝트들의 교집합(&)과 합집합(|)으로 생각할 수 있기 때문이다.
type Test = 'A' | 'B' | 'C';
type MappedTest = { [K in Test]: number };
// 위는 아래와 같다.
type MappedTest = {'A' : number} & {'B' : number} & {'C' : number};
타입들을 합집합 기호(|) 단위로 잘 분해하는 것이 중요하다. 유니온의 각 타입들은 직교하기 때문이다.
type Conference = {type: 'conference', isVirtual: boolean}
type Meetup = {type: 'meetup', isVirtual: false}
type TechEvent = Conference | Meetup
type IsVirtual = {isVirtual: true}
type VirtualEvent = IsVirtual & TechEvent
// 타입 대수를 통해 세 가지 표현을 얻을 수 있다.
type VirtualEvent = IsVirtual & TechEvent
type VirtualEvent = IsVirtual & (Conference | Meetup)
type VirtualEvent = (IsVirtual & Conference) | (IsVirtual & Meetup)
type VirtualEvent = ({isVirtual: true} & {type: 'conference'}) | ({isVirtual: true} & {type: 'meetup'})
이 글을 이해하는데 있어 위 내용만 알고 있으면 충분하지만, 더 자세한 내용은 아래를 참고한다.
https://itchallenger.tistory.com/448?category=1091150
2. keyof 연산자
이미 존재하는 오브젝트를 사용한 타입 지정이 가능하다.
// no index signature
type Point = { x: number; y: number };
type P = keyof Point; // type P = 'x' | 'y'
// with index signature
type Arrayish = { [n: number]: unknown };
type A = keyof Arrayish; // type A = number
// 자바스크립트 오브젝트 키는 스트링 타입으로 반드시 강제변환되기 때문에 숫자도 허용한다.
type Mapish = { [k: string]: boolean };
type M = keyof Mapish; // type M = string | number
3. Mapped Type
기존 정의된 타입을 새로운 타입으로 변환해주는 문법이다.
유니온 타입의Mapped Type
type Test = 'A' | 'B' | 'C';
type MappedTest = { [K in Test]: number };
const mappedTest: MappedTest = {
A: 1,
B: 2,
C: 3,
}
인터페이스, 타입을 이용한 Mapped Type
type Subset<T> = {
[K in keyof T]?: T[K];
}
const mappedTest: Subset<MappedTest> = {
A: 1, // 생략 가능
B: 2, // 생략 가능
C: 3, // 생략 가능
}
예제 : 타입 바꾸기
type OptionsFlags<Type> = {
[Property in keyof Type]: boolean;
};
type FeatureFlags = {
darkMode: () => void;
newUserProfile: () => void;
};
type FeatureOptions = OptionsFlags<FeatureFlags>;
// 위와 동일한 표현임.
type FeatureOptions = {
darkMode: boolean;
newUserProfile: boolean;
}
예제 : 모디파이어 적용 - readonly 제거
mutability(readonly)와 optionality(?)를 적용할 수 있다.
// 'readonly' 속성 제거
type CreateMutable<Type> = {
-readonly [Property in keyof Type]: Type[Property]; // +readonly하면 추가함. default는 mutable(-readonly)
};
// readonly 타입
type LockedAccount = {
readonly id: string;
readonly name: string;
};
type UnlockedAccount = CreateMutable<LockedAccount>;
// 변환된 타입. mutable 타입.
type UnlockedAccount = {
id: string;
name: string;
}
예제 : Optionality 제거
// 'optional' 제거. 전부 필수 필드로
type Concrete<Type> = {
[Property in keyof Type]-?: Type[Property]; // +?하면 optional하게 바꿈. default는 필수
};
// 'optional' 추가
type Optional<Type> = {
[Property in keyof Type]+?: Type[Property];
};
type MaybeUser = {
id: string;
name?: string;
age?: number;
};
type User = Concrete<MaybeUser>;
// 위와 같음
type User = {
id: string;
name: string;
age: number;
}
예제 : template literal 이용하여 기존 필드명 이용해 새로운 프로퍼티 만들기. (get + 필드명)
// template literal types 처럼 새로운 프로퍼티 이름 만들기.
type Getters<Type> = {
[Property in keyof Type as `get${Capitalize<string & Property>}`]: () => Type[Property]
};
interface Person {
name: string;
age: number;
location: string;
}
type LazyPerson = Getters<Person>;
// 위와 같음
type LazyPerson = {
getName: () => string;
getAge: () => number;
getLocation: () => string;
}
예제 : 속성명으로 해당 속성 제거하기 - as 사용
as 뒤의 타입이 never로 평가되면 해당 키가 날라간다.
// 해당 타입의 키들에 필터를 거는것처럼 작동한다.
type RemoveKindField<Type> = {
[Property in keyof Type as Exclude<Property, "kind">]: Type[Property]
};
interface Circle {
kind: "circle";
radius: number;
type KindlessCircle = RemoveKindField<Circle>;
// 위와 같음
type KindlessCircle = {
radius: number;
}
예제 : value에 해당 키가 존재하는지 확인하기
extends는 마치 등호처럼 사용된다. value가 {pil:true} 타입에 할당 가능하면 해당 키의 값을 true, false로 매핑한다.
type ExtractPII<Type> = {
[Property in keyof Type]: Type[Property] extends { pii: true } ? true : false;
};
type DBFields = {
id: { format: "incrementing" };
name: { type: string; pii: true };
};
type ObjectsNeedingGDPRDeletion = ExtractPII<DBFields>;
// 위와 같음
type ObjectsNeedingGDPRDeletion = {
id: false;
name: true;
}
'FrontEnd' 카테고리의 다른 글
TypeORM 스터디 : QueryBuilder 1편 - CRUD 기본 (0) | 2021.10.29 |
---|---|
TypeOrm 스터디 : Active Record Vs Data Mapper. 그리고 Query Builder (0) | 2021.10.29 |
[모던 브라우저 공부]브라우저 하이레벨 아키텍처 (0) | 2021.08.29 |
[프론프엔드 면접 대비] 코어 자바스크립트 : 데이터 타입 (0) | 2021.08.28 |
[프론트엔드 면접 대비] 브라우저의 렌더링 과정 알아보기 (0) | 2021.08.24 |