[Typescript] exercise 7

박세진·2023년 3월 21일
1

원티드 타입스크립트 챌린지를 듣고, 추천 받은 공부 방법 중에 exercise를 통해서 1부터 10까지 풀 수 있어야 된다고 해서, exercises를 풀면서 타입스크립트를 학습해보기로 했다.

사이트 : TypeScript Exercises

exercise7

문제 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}`);
}

Generic

제네릭은 하나의 유형이 아닌 여러 유형에서 작동할 수 있는 컴포넌트를 생성할 수 있다.

function identity<Type>(arg: Type): Type {
	return arg;
}

// Type 변수를 추가해서 매개변수와 반환 타입에 Type을 사용
// 이러한 것을 제네릭 함수라고 함

제네릭 함수를 2가지 방법으로 호출할 수 있다.

  1. 타입 인수를 포함하여 모든 인수를 전달하는 방법
let output = identity<string>('hello');
// <>를 사용하여 Type을 string으로 명시적으로 설정함
  1. 가장 일반적인 방법으로, 타입 인수 추론을 사용한다.
    컴파일러가 전달하는 인수의 타입을 기반으로 Type의 값을 자동으로 설정하도록 하는 것이다. 타입 추론은 코드를 더 짧고 가독성 있게 하는데 도움이 될 수 있지만, 코드가 복잡해져 컴파일러가 타입을 추론하지 못하는 경우 타입을 명시적으로 전달해야 할 수도 있다.
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

Tuple type은 배열 타입의 한 종류로, 포함된 요소의 수와 각 위치에서 포함된 타입을 정확하게 알고 있다. Tuple은 런타임에서 표현되지 않는다.

type StringNumberPair = [string, number];

destructure tuple

function doSomething(stringHash: [string, number]) {
    const [inputString, hash] = stringHash;

    console.log(inputString); // string
    console.log(hash); // numbe
}

optional tuple

튜플에서는 요소의 타입 뒤에 ?를 작성하여 선택적인 속성을 가질 수 있다. 옵셔널 튜플 요소는 끝에만 올 수 있고, 길이의 타입에도 영향을 미친다.

type Either2dOr3d = [number, number, number?];

function setCoordinate(coord: Either2dOr3d) {
    const [x, y, z] = coord;
    console.log(x);
    console.log(y);
    console.log(z);
}

rest element

튜플은 배열/튜플 타입인 나머지 요소(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 tuple

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);

공식문서를 보며, 번역을 하다 보니 전체적으로 글이 매끄럽지 않다...ㅎ...ㅎ

generic과 tuple을 이용해서 문제풀기

2개의 인자를 받아서 역순으로 반환하는 swap 함수를 구현하기 위해서 generic과 tuple을 이용했다.

export function swap<T, V>(v1: T, v2: V): [V, T] {
    return [v2, v1];
}

// generic 변수 T, V
// 역순으로 반환해야 되고 순서에 맞게 맞는 제네릭 타입이 반환되어야 하기 때문에 tuple을 이용함

참고

profile
경험한 것을 기록

0개의 댓글