타입스크립트 공부하기 (하루에 3문제씩)
TypeScript Exercises
A set of interactive TypeScript exercises
typescript-exercises.github.io
Exercise 1
export type User = {
name : string;
age : number;
occupation : string;
};
export const users: User[] = [
{
name: 'Max Mustermann',
age: 25,
occupation: 'Chimney sweep'
},
{
name: 'Kate Müller',
age: 23,
occupation: 'Astronaut'
}
];
export function logPerson(user: User) {
console.log(` - ${user.name}, ${user.age}`);
}
console.log('Users:');
users.forEach(logPerson);
interface와 type alias의 차이
공통점
- 타입 상속 가능
interface
- 객체만 타입 정의 가능
- 동일한 이름으로 타입 정의시 기존 타입 선언에 병합된다
interface A {
a: string;
}
interface B {
b: string;
}
interface C extends A, B {
c: string;
}
const example: C = {
a: "a",
b: "b",
c: "c"
};
type alias
- 모든 타입을 정의 가능 (원시값, 튜플, 유니언, 객체, 제네릭 등)
Exercise 2
interface User {
name: string;
age: number;
occupation: string;
}
interface Admin {
name: string;
age: number;
role: string;
}
export type Person = User | Admin;
export const persons: Person[] /* <- Person[] */ = [
{
name: 'Max Mustermann',
age: 25,
occupation: 'Chimney sweep'
},
{
name: 'Jane Doe',
age: 32,
role: 'Administrator'
},
{
name: 'Kate Müller',
age: 23,
occupation: 'Astronaut'
},
{
name: 'Bruce Willis',
age: 64,
role: 'World saver'
}
];
export function logPerson(user: Person) {
console.log(` - ${user.name}, ${user.age}`);
}
persons.forEach(logPerson);
Union type
하나의 타입에 여러 타입이 올 수 있도록 허용하는 타입
type data : string[] | number[]; // string 배열 또는 number[]
type data : (string | number)[]; // string,number가 혼합된 배열
union타입을 사용하면 타입을 구분하여 타입을 좁혀 사용(narrowing)하는 것이 필요하다
1. 조건문 (if, in, instanceof, typeof)
function process(input: string | number) {
if (typeof input === "string") {
console.log(input.toUpperCase()); // string 메서드 사용 가능
} else {
console.log(input.toFixed(2)); // number 메서드 사용 가능
}
}
2. tag-base union / discriminated union
객체 타입마다 고유한 식별키를 주는 방법
type Circle = { kind: 'circle'; radius: number };
type Square = { kind: 'square'; side: number };
type Shape = Circle | Square;
function getArea(shape: Shape): number {
switch (shape.kind) {
case 'circle':
return Math.PI * shape.radius ** 2;
case 'square':
return shape.side ** 2;
}
}
3. 사용자 정의 타입 가드(type guard)
타입 검사 로직이 많아진다면 value is Type 처럼 타입 판별 함수를 따로 만들어서 사용
type Admin = { role: "admin"; privileges: string[] };
type User = { role: "user"; email: string };
function isAdmin(user: Admin | User): user is Admin {
return user.role === "admin";
}
function handle(user: Admin | User) {
if (isAdmin(user)) {
console.log(user.privileges); // Admin으로 타입 좁혀짐
} else {
console.log(user.email); // User로 좁혀짐
}
}
is 연산자를 통해 반환 타입을 정의해서 사용하는 것도 가능하다
type Cat = { meow: () => void };
type Dog = { bark: () => void };
type Animal = Cat | Dog;
function isDog(animal: Animal): animal is Dog {
return "bark" in animal;
}
function speak(animal: Animal) {
if (isDog(animal)) {
animal.bark(); // Dog
} else {
animal.meow(); // Cat
}
}
is | as | |
역할 | 타입을 판별하는 타입 가드 | 타입을 강제로 단언 (assertion) |
목적 | 런타임에 타입 체크 & 타입 내로잉 | 컴파일러에게 "이건 이 타입이야"라고 주장 |
안전성 | 안전: 판별 로직이 있어야 함 | 위험: 잘못 사용하면 런타임 에러 발생 가능 |
사용 위치 | 함수 반환 타입 (value is Type) | 값 뒤에 붙이는 단언 (value as Type) |
타입스크립트가 추론하는가 | ✅ 네. 타입스크립트가 타입을 내로잉함 | ❌ 아니요. 타입스크립트가 믿고 따름 |
Exercise 3
interface User {
name: string;
age: number;
occupation: string;
}
interface Admin {
name: string;
age: number;
role: string;
}
export type Person = User | Admin;
export const persons: Person[] = [
{
name: 'Max Mustermann',
age: 25,
occupation: 'Chimney sweep'
},
{
name: 'Jane Doe',
age: 32,
role: 'Administrator'
},
{
name: 'Kate Müller',
age: 23,
occupation: 'Astronaut'
},
{
name: 'Bruce Willis',
age: 64,
role: 'World saver'
}
];
export function logPerson(person: Person) {
let additionalInformation: string;
if ('role' in person) {
additionalInformation = person.role;
} else {
additionalInformation = person.occupation;
}
console.log(` - ${person.name}, ${person.age}, ${additionalInformation}`);
}
persons.forEach(logPerson);
in 연산자
객체 속성 여부를 확인하는 연산자
Exercise 4
interface User {
type: 'user';
name: string;
age: number;
occupation: string;
}
interface Admin {
type: 'admin';
name: string;
age: number;
role: string;
}
export type Person = User | Admin;
export const persons: Person[] = [
{ type: 'user', name: 'Max Mustermann', age: 25, occupation: 'Chimney sweep' },
{ type: 'admin', name: 'Jane Doe', age: 32, role: 'Administrator' },
{ type: 'user', name: 'Kate Müller', age: 23, occupation: 'Astronaut' },
{ type: 'admin', name: 'Bruce Willis', age: 64, role: 'World saver' }
];
export function isAdmin(person: Person) : person is Admin {
return person.type === 'admin';
}
export function isUser(person: Person) : person is User {
return person.type === 'user';
}
export function logPerson(person: Person) {
let additionalInformation: string = '';
if (isAdmin(person)) {
additionalInformation = person.role;
}
if (isUser(person)) {
additionalInformation = person.occupation;
}
console.log(` - ${person.name}, ${person.age}, ${additionalInformation}`);
}
console.log('Admins:');
persons.filter(isAdmin).forEach(logPerson);
console.log('Users:');
persons.filter(isUser).forEach(logPerson);
사용자 정의 타입 가드
타입스크립트에서 제공하는 타입 가드(ex typeof, Array.isArray)가 아니라,
사용자 정의 타입 가드는 명시적으로 타입을 알려주어야 한다
함수 정의 방식 | 타입스크립트의 반응 |
function isAdmin(p: Person): boolean | true여도 타입 좁혀지지 않음 (아직 Person) |
function isAdmin(p: Person): p is Admin | true면 p가 Admin임을 확신하고 좁힘 |
Exercise 5
interface User {
type: 'user';
name: string;
age: number;
occupation: string;
}
interface Admin {
type: 'admin';
name: string;
age: number;
role: string;
}
export type Person = User | Admin;
export const persons: Person[] = [
{ type: 'user', name: 'Max Mustermann', age: 25, occupation: 'Chimney sweep' },
{
type: 'admin',
name: 'Jane Doe',
age: 32,
role: 'Administrator'
},
{
type: 'user',
name: 'Kate Müller',
age: 23,
occupation: 'Astronaut'
},
{
type: 'admin',
name: 'Bruce Willis',
age: 64,
role: 'World saver'
},
{
type: 'user',
name: 'Wilson',
age: 23,
occupation: 'Ball'
},
{
type: 'admin',
name: 'Agent Smith',
age: 23,
role: 'Administrator'
}
];
export const isAdmin = (person: Person): person is Admin => person.type === 'admin';
export const isUser = (person: Person): person is User => person.type === 'user';
export function logPerson(person: Person) {
let additionalInformation = '';
if (isAdmin(person)) {
additionalInformation = person.role;
}
if (isUser(person)) {
additionalInformation = person.occupation;
}
console.log(` - ${person.name}, ${person.age}, ${additionalInformation}`);
}
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);
유틸리티 타입 (객체 타입을 변형하는 도구)
T = 객체
Keys = 문자열 리터럴 or 문자열 리터럴의 유니언
- Partial<T> : T의 모든 속성을 옵셔널로
- Required<T> : T의 모든 속성을 필수로
- ReadOnly<T> : T의 모든 속성을 읽기 전용으로
- Pick<T, Keys> : T에서 Keys에 해당하는 속성만 뽑아내기 <-> Omit<T, Keys>
- Record<Keys, T> : 모든 속성 타입이 T가 되도록 매핑
- Extract<Union, T> : Union 타입에서 T에 해당하는 타입만 추출 <-> Exclude<Union, T>
- NonNullable<T> : null, undefined 제외 타입
Exclude / Extract | Omit / Pick | |
작동 대상 | 유니언 타입 | 객체 타입 |
결과 | 유니언 타입 | 객체 타입 |
용도 | 타입 필터링 (논리적 조건) | 객체 속성 추출/제외 |
Exercise 6
interface User {
type: 'user';
name: string;
age: number;
occupation: string;
}
interface Admin {
type: 'admin';
name: string;
age: number;
role: string;
}
export type Person = User | Admin;
export const persons: Person[] = [
{ type: 'user', name: 'Max Mustermann', age: 25, occupation: 'Chimney sweep' },
{ type: 'admin', name: 'Jane Doe', age: 32, role: 'Administrator' },
{ type: 'user', name: 'Kate Müller', age: 23, occupation: 'Astronaut' },
{ type: 'admin', name: 'Bruce Willis', age: 64, role: 'World saver' },
{ type: 'user', name: 'Wilson', age: 23, occupation: 'Ball' },
{ type: 'admin', name: 'Agent Smith', age: 23, role: 'Anti-virus engineer' }
];
export function logPerson(person: Person) {
console.log(
` - ${person.name}, ${person.age}, ${person.type === 'admin' ? person.role : person.occupation}`
);
}
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: any, criteria: any): any[] {
return persons
.filter((person) => person.type === personType)
.filter((person) => {
let criteriaKeys = Object.keys(criteria) as (keyof Person)[];
return criteriaKeys.every((fieldName) => {
return person[fieldName] === criteria[fieldName];
});
});
}
export const usersOfAge23 = filterPersons(persons, 'user', { age: 23 });
export const adminsOfAge23 = filterPersons(persons, 'admin', { age: 23 });
console.log('Users of age 23:');
usersOfAge23.forEach(logPerson);
console.log();
console.log('Admins of age 23:');
adminsOfAge23.forEach(logPerson);
함수 오버로딩
동일한 이름을 가지면서, 매개변수 형식 또는 반환 형식이 다른 함수로,
내부 구현은 한번만 하고, 호출할 수 있는 함수의 타입을 미리 여러개 타이핑해두는 것이다
함수 입력값에 따라 반환값이 달라질 때, 정확한 타입 추론을 위해 사용한다.
// 오버로드 시그니처
function getLength(str: string): number;
function getLength(arr: any[]): number;
// 실제 구현
function getLength(value: string | any[]): number {
return value.length;
}
getLength("hello"); // 추론: number
getLength([1, 2, 3]); // 추론: number
단순 인자 타입이 다르고, 처리 방식이 같고 & 반환 타입도 같다면 제네릭을 사용하는 것이 좋다
Exercise 7
interface User {
type: 'user';
name: string;
age: number;
occupation: string;
}
interface Admin {
type: 'admin';
name: string;
age: number;
role: string;
}
function logUser(user: User) {
const pos = users.indexOf(user) + 1;
console.log(` - #${pos} User: ${user.name}, ${user.age}, ${user.occupation}`);
}
function logAdmin(admin: Admin) {
const pos = admins.indexOf(admin) + 1;
console.log(` - #${pos} Admin: ${admin.name}, ${admin.age}, ${admin.role}`);
}
const admins: Admin[] = [
{
type: 'admin',
name: 'Will Bruces',
age: 30,
role: 'Overseer'
},
{
type: 'admin',
name: 'Steve',
age: 40,
role: 'Steve'
}
];
const users: User[] = [
{
type: 'user',
name: 'Moses',
age: 70,
occupation: 'Desert guide'
},
{
type: 'user',
name: 'Superman',
age: 28,
occupation: 'Ordinary person'
}
];
export function swap<T1, T2>(v1 : T1, v2: T2) : [T2, T1] {
return [v2, v1];
}
function test1() {
console.log('test1:');
const [secondUser, firstAdmin] = swap(admins[0], users[1]);
logUser(secondUser);
logAdmin(firstAdmin);
}
function test2() {
console.log('test2:');
const [secondAdmin, firstUser] = swap(users[0], admins[1]);
logAdmin(secondAdmin);
logUser(firstUser);
}
function test3() {
console.log('test3:');
const [secondUser, firstUser] = swap(users[0], users[1]);
logUser(secondUser);
logUser(firstUser);
}
function test4() {
console.log('test4:');
const [firstAdmin, secondAdmin] = swap(admins[1], admins[0]);
logAdmin(firstAdmin);
logAdmin(secondAdmin);
}
function test5() {
console.log('test5:');
const [stringValue, numericValue] = swap(123, 'Hello World');
console.log(` - String: ${stringValue}`);
console.log(` - Numeric: ${numericValue}`);
}
[test1, test2, test3, test4, test5].forEach((test) => test());
튜플 (tuple)
배열의 길이와 각 요소의 타입을 정확히 지정할 수 있는 배열 타입이다
구조가 정해진 배열에서는 배열보다 정밀한 제어를 할 수 있다
그리고 튜플도 배열이기 때문에 push와 같은 배열 메소드 사용이 가능하다 (as const 또는 리터럴 제한 필요)
배열 (Array) | 튜플 (Tuple) | |
타입 | 모두 동일 | 각 요소마다 다를 수 있음 |
길이 | 유동적 | 고정되거나 제한될 수 있음 |
순서 | 중요하지 않음 | 중요함 |
제네릭 (generic)
여러 타입에서 동작할 수 있도록 만드는 타입 변수
- 여러 타입에서 재사용될 때
- 리턴 타입이 입력 타입과 관련있을 때
- 응답 타입처럼 타입 안정성을 유지하고 싶을 때
- 라이브러리나 유틸 함수를 만들때
// 함수
function wrapInArray<T>(value: T): T[] {
return [value];
}
const result = wrapInArray("hi"); // string[]
// 인터페이스
interface ApiResponse<T> {
success: boolean;
data: T;
}
const response: ApiResponse<string[]> = {
success: true,
data: ["a", "b"],
};
제네릭의 제약 조건 (T extends P)
제네릭 타입이 "어떤 속성을 가져야 한다"는 제한을 거는 걸어, 특정 구조를 가진 타입만 받도록 제한하는 것이다.
function getLength<T extends { length: number }>(value: T): number {
return value.length; // length 속성이 있는 타입만 받도록 제약조건 설정
}
getLength("hello"); // OK
getLength([1, 2, 3]); // OK
getLength({ length: 10 })// OK
getLength(123); // ❌ number는 length 없음
제네릭 + 제약 조건에서는 name 속성만 있으면, Person보다 더 구체적인 타입은 입력할 수 있다
// 1. 인터페이스만 직접 받는 경우
interface Person {
name: string;
}
function greet(p: Person) {
console.log("Hello, " + p.name);
}
// 2. 제네릭 + 제약 조건 (T extends Person)
interface Person {
name: string;
}
function greet<T extends Person>(p: T) {
console.log("Hello, " + p.name);
}
greet({ name: "Alice" }); // OK
greet({ name: "Bob", age: 30 }); // OK (추가 속성 가능)
greet({ age: 20 }); // ❌ name 없음
Exercise 8
interface User {
type: 'user';
name: string;
age: number;
occupation: string;
}
interface Admin {
type: 'admin';
name: string;
age: number;
role: string;
}
type PowerUser = {
type : 'powerUser'
} & Omit<User, 'type'> & Omit<Admin, 'type'>
export type Person = User | Admin | PowerUser;
export const persons: Person[] = [
{ type: 'user', name: 'Max Mustermann', age: 25, occupation: 'Chimney sweep' },
{ type: 'admin', name: 'Jane Doe', age: 32, role: 'Administrator' },
{ type: 'user', name: 'Kate Müller', age: 23, occupation: 'Astronaut' },
{ type: 'admin', name: 'Bruce Willis', age: 64, role: 'World saver' },
{
type: 'powerUser',
name: 'Nikki Stone',
age: 45,
role: 'Moderator',
occupation: 'Cat groomer'
}
];
function isAdmin(person: Person): person is Admin {
return person.type === 'admin';
}
function isUser(person: Person): person is User {
return person.type === 'user';
}
function isPowerUser(person: Person): person is PowerUser {
return person.type === 'powerUser';
}
export function logPerson(person: Person) {
let additionalInformation: string = '';
if (isAdmin(person)) {
additionalInformation = person.role;
}
if (isUser(person)) {
additionalInformation = person.occupation;
}
if (isPowerUser(person)) {
additionalInformation = `${person.role}, ${person.occupation}`;
}
console.log(`${person.name}, ${person.age}, ${additionalInformation}`);
}
console.log('Admins:');
persons.filter(isAdmin).forEach(logPerson);
console.log();
console.log('Users:');
persons.filter(isUser).forEach(logPerson);
console.log();
console.log('Power users:');
persons.filter(isPowerUser).forEach(logPerson);
인터섹션 타입(&)
여러 객체 타입을 하나로 결합하는 타입으로, 유니온과 달리 결합한 타입을 모두 만족해야 한다
type A = { value: string };
type B = { value: number };
type C = A & B; // value : string이면서 동시에 number은 불가능하므로 never 타입
const c: C = { value: ??? }; // ❌
Exercise 9
interface User {
type: 'user';
name: string;
age: number;
occupation: string;
}
interface Admin {
type: 'admin';
name: string;
age: number;
role: string;
}
type Person = User | Admin;
const admins: Admin[] = [
{ type: 'admin', name: 'Jane Doe', age: 32, role: 'Administrator' },
{ type: 'admin', name: 'Bruce Willis', age: 64, role: 'World saver' }
];
const users: User[] = [
{ type: 'user', name: 'Max Mustermann', age: 25, occupation: 'Chimney sweep' },
{ type: 'user', name: 'Kate Müller', age: 23, occupation: 'Astronaut' }
];
export type ApiResponse<T> =
| { status: 'success'; data: T; }
| { status: 'error'; error: string; };
export function requestAdmins(callback: (response: ApiResponse<Admin[]>) => void) {
callback({
status: 'success',
data: admins
});
}
export function requestUsers(callback: (response: ApiResponse<User[]>) => void) {
callback({
status: 'success',
data: users
});
}
export function requestCurrentServerTime(callback: (response: ApiResponse<number>) => void) {
callback({
status: 'success',
data: Date.now()
});
}
export function requestCoffeeMachineQueueLength(callback: (response: ApiResponse<number>) => void) {
callback({
status: 'error',
error: 'Numeric value has exceeded Number.MAX_SAFE_INTEGER.'
});
}
function logPerson(person: Person) {
console.log(
` - ${person.name}, ${person.age}, ${person.type === 'admin' ? person.role : person.occupation}`
);
}
function startTheApp(callback: (error: Error | null) => void) {
requestAdmins((adminsResponse) => {
console.log('Admins:');
if (adminsResponse.status === 'success') {
adminsResponse.data.forEach(logPerson);
} else {
return callback(new Error(adminsResponse.error));
}
console.log();
requestUsers((usersResponse) => {
console.log('Users:');
if (usersResponse.status === 'success') {
usersResponse.data.forEach(logPerson);
} else {
return callback(new Error(usersResponse.error));
}
console.log();
requestCurrentServerTime((serverTimeResponse) => {
console.log('Server time:');
if (serverTimeResponse.status === 'success') {
console.log(` ${new Date(serverTimeResponse.data).toLocaleString()}`);
} else {
return callback(new Error(serverTimeResponse.error));
}
console.log();
requestCoffeeMachineQueueLength((coffeeMachineQueueLengthResponse) => {
console.log('Coffee machine queue length:');
if (coffeeMachineQueueLengthResponse.status === 'success') {
console.log(` ${coffeeMachineQueueLengthResponse.data}`);
} else {
return callback(new Error(coffeeMachineQueueLengthResponse.error));
}
callback(null);
});
});
});
});
}
startTheApp((e: Error | null) => {
console.log();
if (e) {
console.log(`Error: "${e.message}", but it's fine, sometimes errors are inevitable.`)
} else {
console.log('Success!');
}
});
제네릭을 사용해서 성공/실패 ApiResponse을 하나로 타입으로 묶었다
Exercise 10 (아직 못풀었음)
1차 시도
배열을 입력받는 requestAdmins, requestUsers에는 적용이되지만,
interface User {
type: 'user';
name: string;
age: number;
occupation: string;
}
interface Admin {
type: 'admin';
name: string;
age: number;
role: string;
}
type Person = User | Admin;
const admins: Admin[] = [
{ type: 'admin', name: 'Jane Doe', age: 32, role: 'Administrator' },
{ type: 'admin', name: 'Bruce Willis', age: 64, role: 'World saver' }
];
const users: User[] = [
{ type: 'user', name: 'Max Mustermann', age: 25, occupation: 'Chimney sweep' },
{ type: 'user', name: 'Kate Müller', age: 23, occupation: 'Astronaut' }
];
export type ApiResponse<T> = (
{
status: 'success';
data: T;
} |
{
status: 'error';
error: string;
}
);
// 상태 코드 객체를 반환하는 콜백을 받는 콜백을 받음
export function promisify<T>(fn: (callback: (response: ApiResponse<T>) => void) => void): Promise<T> {
return new Promise((resolve, reject) => {
fn(res => {
if(res.status === 'success') resolve(res.data)
else reject(res.error)
})
})
}
const oldApi = {
requestAdmins(callback: (response: ApiResponse<Admin[]>) => void) {
callback({
status: 'success',
data: admins
});
},
requestUsers(callback: (response: ApiResponse<User[]>) => void) {
callback({
status: 'success',
data: users
});
},
requestCurrentServerTime(callback: (response: ApiResponse<number>) => void) {
callback({
status: 'success',
data: Date.now()
});
},
requestCoffeeMachineQueueLength(callback: (response: ApiResponse<number>) => void) {
callback({
status: 'error',
error: 'Numeric value has exceeded Number.MAX_SAFE_INTEGER.'
});
}
};
export const api = {
requestAdmins: promisify(oldApi.requestAdmins),
requestUsers: promisify(oldApi.requestUsers),
requestCurrentServerTime: promisify(oldApi.requestCurrentServerTime),
requestCoffeeMachineQueueLength: promisify(oldApi.requestCoffeeMachineQueueLength)
};
function logPerson(person: Person) {
console.log(
` - ${person.name}, ${person.age}, ${person.type === 'admin' ? person.role : person.occupation}`
);
}
async function startTheApp() {
console.log('Admins:');
(await api.requestAdmins()).forEach(logPerson);
console.log();
console.log('Users:');
(await api.requestUsers()).forEach(logPerson);
console.log();
console.log('Server time:');
console.log(` ${new Date(await api.requestCurrentServerTime()).toLocaleString()}`);
console.log();
console.log('Coffee machine queue length:');
console.log(` ${await api.requestCoffeeMachineQueueLength()}`);
}
startTheApp().then(
() => {
console.log('Success!');
},
(e: Error) => {
console.log(`Error: "${e.message}", but it's fine, sometimes errors are inevitable.`);
}
);
Promise<T>
resolve()에 넘겨주는 값(= 성공 결과)의 타입을 넘겨준다
reject는 타입이 강제되지 않기 때문에, any 타입으로 취급된다
Exercise 11
type alias로 타입 중복 피하기
declare module 'str-utils' {
type StrTransfrom = (value: string) => string;
export const strReverse:StrTransfrom;
export const strToLower:StrTransfrom;
export const strToUpper:StrTransfrom;
export const strRandomize:StrTransfrom;
export const strInvertCase:StrTransfrom;
}
타입스크립트 모듈 정의 파일 (.d.ts)
실제 코드를 포함하지 않고, 타입 정보(함수 시그니처, 변수 타입, 인터페이스 등)만 담고 있는 파일
외부 js 라이브러리에 타입을 덧씌울 때 declare module이 필요하다
내 코드 안에서 쓸 타입이면 그냥 export type, export function 등만 쓰면 된다
타입은 대문자로 시작하는 카멜케이스
// declarations/str-utils/index.d.ts
declare module 'str-utils' {
export function reverse(str: string): string;
}
Exercise 12
Exercise 13
Exercise 14
Exercise 15
Exercise 16
'🐠 FrontEnd > JS & TS' 카테고리의 다른 글
tanstackQuery에서 useQuery, useMutation 타입 지정하기 (0) | 2024.07.23 |
---|---|
[TS] type alias과 interface 차이와 index signature (0) | 2024.07.03 |
비슷해서 헷갈리는 Rest 파라미터, spread 연산자 (0) | 2024.07.02 |
고유 식별자를 생성하는 uuid와 crypto.randomUUID (0) | 2024.05.28 |