원티드 타입스크립트 챌린지를 듣고, 추천 받은 공부 방법 중에 exercise를 통해서 1부터 10까지 풀 수 있어야 된다고 해서, exercises를 풀면서 타입스크립트를 학습해보기로 했다.
사이트 : TypeScript Exercises
문제 7: 2명의 person을 받아서 역순으로 반환하는 swap 함수를 구현하기. swap 함수에 적절한 타입을 제공하도록 변경해줘야 된다. Person type에만 국한될 필요 없이, 지정된 두 가지 타입 중 어떤 것이든 작동하도록 타입을 지정하기.
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(v1, v2) {
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}`);
}
제네릭은 하나의 유형이 아닌 여러 유형에서 작동할 수 있는 컴포넌트를 생성할 수 있다.
function identity<Type>(arg: Type): Type {
return arg;
}
// Type 변수를 추가해서 매개변수와 반환 타입에 Type을 사용
// 이러한 것을 제네릭 함수라고 함
제네릭 함수를 2가지 방법으로 호출할 수 있다.
let output = identity<string>('hello');
// <>를 사용하여 Type을 string으로 명시적으로 설정함
let output = identity('hello');
// 문자열이 전달된 것을 추론함
제네릭 함수는 비제네릭 함수처럼 타입 매개변수가 먼저 나열되고, 함수 선언이 유사하다.
function identity<Type>(arg: Type): Type {
return arg;
}
let myIdentity: <Type>(arg: Type) => Type = identity;
// <Type>(arg: Type) => Type 이 타입부분
// 타입부분의 문법 (arg: ArgType) => ReturnType;
// let myIdentity = identity
타입 변수의 수와 타입 변수가 사용되는 방식이 일치하면, 제네릭 타입 매개변수의 이름을 다르게 사용할 수 있다
let myIdentity: <Input>(arg: Input) => Input = identity;
제네릭 타입을 객체 리터럴 타입의 콜시그니처로 작성할 수도 있고, 제네릭 인터페이스로 바꿔서 작성할 수도 있다.
// 객체 리터럴 타입의 콜시그니처
let myIdentity: { <Type>(arg: Type): Type } = identity;
// 제네릭 인터페이스
interface GenericIdentityFn {
<Type>(arg: Type): Type;
}
function identity<Type>(arg: Type): Type {
return arg;
}
let myIdentity: GenericIdentityFn = identity;
// 제네릭 타입의 부분인 비제네릭 콜시그니처
interface GenericIdentityFn2<Type> {
<Type>(arg: Type): Type;
}
let myIdentity: GenericIdentityFn2<number> = identity;
Tuple type은 배열 타입의 한 종류로, 포함된 요소의 수와 각 위치에서 포함된 타입을 정확하게 알고 있다. Tuple은 런타임에서 표현되지 않는다.
type StringNumberPair = [string, number];
function doSomething(stringHash: [string, number]) {
const [inputString, hash] = stringHash;
console.log(inputString); // string
console.log(hash); // numbe
}
튜플에서는 요소의 타입 뒤에 ?
를 작성하여 선택적인 속성을 가질 수 있다. 옵셔널 튜플 요소는 끝에만 올 수 있고, 길이의 타입에도 영향을 미친다.
type Either2dOr3d = [number, number, number?];
function setCoordinate(coord: Either2dOr3d) {
const [x, y, z] = coord;
console.log(x);
console.log(y);
console.log(z);
}
튜플은 배열/튜플 타입인 나머지 요소(rest elements)를 가질 수 있다.
type StringNumberBooleans = [string, number, ...boolean[]];
type StringBooleansNumber = [string, ...boolean[], number];
type BooleansStringNumber = [...boolean[], string, number];
const a: StringNumberBooleans = ["hello", 1];
const b: StringNumberBooleans = ["beautiful", 2, true];
const c: StringNumberBooleans = ["world", 3, true, false, true, false, true];
function readButtonInput(...args: [string, number, ...boolean[]]) {
const [name, version, ...input] == args;
}
readonly
를 앞에 붙여서 속성을 읽기전용으로 지정할 수 있다.
function doSomething(pair: readonly [string, number] { ... }
as const
를 사용하여 readonly
로 추론될 수 있다.
let point = [3, 4] as const
function distanceFromOrigin([x, y]: [number, number]) {
return Math.sqrt(x ** 2 + y ** 2);
}
distanceFromOrigin(point);
공식문서를 보며, 번역을 하다 보니 전체적으로 글이 매끄럽지 않다...ㅎ...ㅎ
2개의 인자를 받아서 역순으로 반환하는 swap 함수를 구현하기 위해서 generic과 tuple을 이용했다.
export function swap<T, V>(v1: T, v2: V): [V, T] {
return [v2, v1];
}
// generic 변수 T, V
// 역순으로 반환해야 되고 순서에 맞게 맞는 제네릭 타입이 반환되어야 하기 때문에 tuple을 이용함
참고