
타입스크립트를 처음 접했을 때 가장 막막했던 건,
"이걸 어디서부터 어떻게 적용해야 하지?" 였습니다.하지만 이전에 자바스크립트 외 다른 언어(Java, C 등)를 접해본 분이라면,
이 exercise를 풀어보고, 여기서 제공해주는 공식 핸디북만으로도 타입스크립트를 프로젝트에 적용하는 데 충분하다고 느꼈어요.저도 아직 타입스크립트를 능숙하게 다루는 단계는 아니지만,
공부하면서 정리해두면 좋겠다고 생각한 개념들과 풀이를 노트처럼 기록해보았습니다.아직 14단계까지는 다 풀지 못했지만..! 💦 공부하면서 천천히 채워나갈 예정이에요.
함께 배우고 성장해봐요! 👩💻🌱
1,2번 문제는 따로 풀이를 적지 않았습니다!!
in operatorin 연산자를 사용해 객체에 특정 속성이 존재하는지 검사하면 타입을 구분할 수 있음 ✅ 예제 1: 기본적인 타입 좁히기
type Fish = { swim: () => void };
type Bird = { fly: () => void };
function move(animal: Fish | Bird) {
if ("swim" in animal) {
return animal.swim();
}
return animal.fly();
}
animal은 Fish 또는 Bird"swim" in animal이면 → Fish로 좁혀짐Bird로 좁혀짐✅ 예제 2: optional 속성 포함된 타입
type Human = { swim?: () => void; fly?: () => void };
type Fish = { swim: () => void };
type Bird = { fly: () => void };
function move(animal: Fish | Bird | Human) {
if ("swim" in animal) {
// animal: Fish | Human
} else {
// animal: Bird | Human
}
}
Human은 swim, fly 둘 다 선택적(optional) 속성"swim" in animal이면 → Fish | Human"swim" not in animal이면 → Bird | Humanexport function logPerson(person: Person) {
let additionalInformation: string;
// **in** 이라는 키워드를 사용해서 타입 좁히기
if ('role' in person) {
additionalInformation = person.role;
} else {
additionalInformation = person.occupation;
}
console.log(` - ${person.name}, ${person.age}, ${additionalInformation}`);
}
Admin 타입과 User 타입 모두를 받는 Person 타입 변수에서 Admin만 골라내고 싶다면,isAdmin을 만들고, person is Admin 형태로 명시한다.export function isAdmin(person: Person): **person is Admin** {
return person.type === 'admin';
}
export function isUser(person: Person): person is User {
return person.type === 'user';
}
Partial<Type>📂 예제 코드 보기🔧 그 외 다양한 Utility Types
Required<T>: 모든 속성을 필수로Readonly<T>: 모든 속성을 읽기 전용으로Pick<T,K>: 특정 속성만 골라서 사용Omit<T,K>: 특정 속성을 제외하고 사용Record<K,T>: K를 key로, T를 value 타입으로 갖는 객체 생성Exclude<T,U>: T에서 U를 제외Extract<T,U>: T에서 U와 겹치는 타입만 추출
export function filterUsers(persons: Person[], criteria: **Partial<User>**): User[] {
return persons.filter(isUser).filter((user) => {
const criteriaKeys = Object.keys(criteria) as (keyof User)[];
return criteriaKeys.every((fieldName) => {
return user[fieldName] === criteria[fieldName];
});
});
}
console.log('Users of age 23:');
filterUsers(
persons,
{
age: 23
}
).forEach(logPerson);
📌 예시
📂 오버로드 시그니처 & 구현 예시// 시그니처 선언
function fn(x: string): void;
function fn(x: number): void;
// 실제 구현부 (모든 경우 커버)
function fn(x: string | number) {
console.log(x);
}
📂 filterPersons 예제 코드
// 시그니처 선언
export function filterPersons(persons: Person[], personType: 'user', criteria: Partial<User>): User[];
export function filterPersons(persons: Person[], personType: 'admin', criteria: Partial<Admin>): Admin[];
// 구현부
export function filterPersons(persons: Person[], personType: 'user' | 'admin', criteria: Partial<Person>): Person[] {
return persons
.filter((person) => person.type === personType)
.filter((person) => {
const criteriaKeys = Object.keys(criteria) as (keyof Person)[];
return criteriaKeys.every((fieldName) => {
return person[fieldName] === criteria[fieldName];
});
});
}
📘 tuple-types 핸디북
📘 generics 핸디북
type Pair = [string, number]export function swap<T, U>(...args: [T, U]): [U, T] {
const [v1, v2] = args;
return [v2, v1];
}
&)&): 여러 타입을 합친 새로운 타입을 생성User와 Admin 타입을 결합해서 PowerUser 타입 생성type PowerUser = Omit<User & Admin, 'type'> & {
type: 'powerUser';
};
declare module 구문 사용declare module 'str-utils' {
export function strReverse(value: string): string;
export function strToLower(value: string): string;
export function strToUpper(value: string): string;
export function strRandomize(value: string): string;
export function strInvertCase(value: string): string;
}
promisify() 함수는 callback을 받아 내부에서 resolve/reject 처리함export function promisify<T>(
arg: (callback: (response: ApiResponse<T>) => void) => void
): () => Promise<T> {
return () => new Promise<T>((resolve, reject) => {
arg((res) => {
if (res.status === 'success') {
resolve(res.data);
} else {
reject(new Error(res.error));
}
});
});
}
📂 사용 예시
const oldApi = {
requestAdmins(callback: (response: ApiResponse<Admin[]>) => void) {
callback({ status: 'success', data: admins });
},
};
export const api = {
requestAdmins: promisify(oldApi.requestAdmins),
};
await api.requestAdmins().then(console.log);
const oldApi = {
requestAdmins(callback: (response: ApiResponse<Admin[]>) => void) {
callback({ status: 'success', data: admins });
},
};
export const api = {
requestAdmins: promisify(oldApi.requestAdmins),
};
await api.requestAdmins().then(console.log);
declare module "모듈명" 구문을 통해, 실제 존재하는 외부 모듈을 타입스크립트에 알려줌.d.ts 파일에서 선언하고, 타입 자동완성/체크에 도움을 줌import {
strReverse,
strToLower,
strToUpper,
strRandomize,
strInvertCase
} from 'str-utils';
declare module 'str-utils' {
export function strReverse(value: string): string;
export function strToLower(value: string): string;
export function strToUpper(value: string): string;
export function strRandomize(value: string): string;
export function strInvertCase(value: string): string;
}
declare module 'stats' {
export function getMaxIndex<T>(input: T[], comparator: (a: T, b: T) => number): number;
export function getMaxElement<T>(input: T[], comparator: (a: T, b: T) => number): null | T;
export function getMinIndex<T>(input: T[], comparator: (a: T, b: T) => number): number;
export function getMinElement<T>(input: T[], comparator: (a: T, b: T) => number): null | T;
export function getMedianIndex<T>(input: T[], comparator: (a: T, b: T) => number): number;
export function getMedianElement<T>(input: T[], comparator: (a: T, b: T) => number): null | T;
export function getAverageValue<T>(input: T[], getValue: (a: T) => number): null | number;
}
📂 stats 모듈 내부 구현 보기
function getMaxIndex(input, comparator) {
if (input.length === 0) return -1;
let maxIndex = 0;
for (let i = 1; i < input.length; i++) {
if (comparator(input[i], input[maxIndex]) > 0) {
maxIndex = i;
}
}
return maxIndex;
}
function getMaxElement(input, comparator) {
const index = getMaxIndex(input, comparator);
return index === -1 ? null : input[index];
}
// ... (다른 함수도 유사한 로직)
module.exports = {
getMaxIndex,
getMaxElement,
getMinIndex,
getMinElement,
getMedianIndex,
getMedianElement,
getAverageValue
};
declare module 내에서 interface를 다시 선언하면 병합됨declare namespace dateWizard {
interface DateDetails {
year: number;
month: number;
date: number;
}
function dateDetails(date: Date): DateDetails;
function utcDateDetails(date: Date): DateDetails;
}
14부터는 뭔소리인지 도저히 모르겠더라고요 ..
공부를 더 하고 다시 추가하러 오도록 하겠습니닷 !!!!!