관리 메뉴

Jerry

[TIL] [타임어택9기][리뉴얼] 타입스크립트 올인원 : Part1. 기본 문법편 - (5) : 원시 래퍼 타입, 템플릿 리터럴 타입, rest 튜플 / enum, keyof, typeof / union(|)과 intersection(&) / 타입 애일리어스와 인터페이.. 본문

Front/Typescript

[TIL] [타임어택9기][리뉴얼] 타입스크립트 올인원 : Part1. 기본 문법편 - (5) : 원시 래퍼 타입, 템플릿 리터럴 타입, rest 튜플 / enum, keyof, typeof / union(|)과 intersection(&) / 타입 애일리어스와 인터페이..

juicyjerry 2024. 2. 27. 00:31
반응형
섹션 1 기본 문법 배우기

 

 

 

 

<원시 래퍼 타입, 템플릿 리터럴 타입, rest 튜플>

 

 

원시 래퍼 타입

const a: string = 'hello';
const b: String = 'hello';

 

 

a와 b는 서로 다른 타입이다.

b는 랩퍼 객체로, new String(); 이런 식으로 사용하는 객체이며, 평소에 잘 사용하지 않는다.

그러니, 대소문자 구분을 잘하자.

 

 

참고로, 타입스크립트는 자동 완성 추천 기능도 제공 ( ctrl + space )

 

 

템플릿 리터럴 활용

type World = "world" | "hell";
const c: World = 'world';

const d = `hello ${c}`;
type Greeting = `hello ${World}`;

 

 

rest parameter 활용

let arr: string[] = [];
let arr2: Array<string> = [];
function rest(a, ...args: string[]) {
    console.log(args); // 1, 2, 3
}

rest('1', '2', '3');

 

 

튜플 활용

const tuple: [string, number] = ['1', 1];
tuple[2] = 'hello';
tuple.push('hello');

 

타입스크립트가 "tuple[2] = 'hello';" 는 막지만, "tuple.push('hello');"는 막지 못 하는 단점이 존재

 

 

 

 

 


 

 

 

 

 

<enum, keyof, typeof>

 

 

 

 

enum 타입 : 보통 변수들을 하나의 그룹으로 묶고 싶을 때 사용

const enum EDirection {
    Up = 3,
    Down = 5,
    Left = 1,
    Right = 0,
}

 

 

 

as const 는 js로 변환 시 남아있다

남아있게 하고 싶으면 객체, 아니면 enum, 모르겠으면 남겨라

만약, 타입스크립트가 바보같이 행동하면 직접 나서야 한다.

const f = EDirection.Up;
const g = EDirection.Down;

const ODirection = {
    Up : 3,
    Down : 5,
    Left : 1,
    Right : 0,
} as const;

 

 

 

실제 enum 쓰는 코드

function walk(dir: EDirection) {}

// enum 쓰기 싫다,  #4처럼 해야 되는데 타입 정의가 복잡해진다
type Direction = typeof ODirection[keyof typeof ODirection];

function run(dir: EDirection) {} // dir은 네 개중에 하나여야 한다라는 뜻

walk(EDirection.Left);
run(ODirection.Right);

 

 

 

keyof에 대해서

const obj = { a: '123', b: 'hello', c: 'world'} as const; // as const 아주 엄격하게 타입핑해준다
type Key = keyof typeof obj; // key들만 가져오고 싶을 경우
type Key = typeof obj[keyof typeof obj]; // value들만 가져오고 싶을 경우

 

 

 

 

 

 


 

 

 

 

 

<union(|)과 intersection(&)>

 

 

 

 

 

 

둘 중에 뭐가 낫냐

간단하게 하려면 타입, 객체지향 프로그래밍하고 싶을 때 interface

type A = {a: string};
const a: A = {a: 'hello'};
const a: {a: string} = {A: 'hello'};

interface B { a: string};
const b: B = { a: 'hello'};

 

 

 

union : 모든 경우의 수를 고려한다 

function add(x: string | number, y: string | number): string | number {return x + y};

const result: string | number = add(1, 2);

add(1, 2);
add('1', '2');
add(1, '2');

 

위 add 함수 타입은 잘못되었다.

왜냐하면 add 함수 타입을 허용하면 다음 예시(result)부터 꼬인다

add(1, 2)의 결과가 숫자인데, 타입이 string이 될 수 있는 잘못된 상황이 나타나게 된다

처음 타입 정의를 잘 해야 한다, 안 그러면 줄줄이 꼬인다

 

 

 


type A 코드 상,  string이면서 numnber여야 한다 (X) ; 논리적으로 말이 안 됨

그럼 언제 앰퍼샌드(&)를 사용하는가? ===> 객체

type A = string & number;

type D = { hello: 'world' } & { zero: 'cho' }; // 모든 속성이 있어야 한다 (인터섹션)
const k: D = { hello: 'world', zero: 'cho'};

type F = { hello: 'world' } | { zero: 'cho' }; // 여러개 중에 하나만 있어도 된다 (유니온)
const t: D = { hello: 'world'};

 

 

 

 

 

 


 

 

 

 

<타입 애일리어스와 인터페이스의 상속(extends)>

 

 

 

 

 

 

상속의 예제

type Animal = { breath: true };
type Mammal = Animal & { breed: true };
type Human = Mammal & { think: true };

const zerocho: Human = { breath: true, breed: true, think: true };

interface A {
    breath: true
}
interface B extends A {
    breed: true
}
const b: B = { breath: true, breed: true };

 

 

 

 

 

 

둘 중에 뭘 사용할지 선택하기만 된
간단 - type, 객체지향 - interface

interface A {
    talk: () => void;
}
interface A {
    eat: () => void;
}
interface A {
    shit: () => void;
}

// 보통 라이브러리들은 인터페이스로 , 확장이 가능하다, 서로간에 합쳐질 수 있다
const a : A = { talk() {}, eat() {}, shit() {}, sleep() {} }

interface A {
    sleep: () => void;
}

 

 

 

네이밍

interface IProps {}
type TAlias = string | number;
enum EHello {
    Left,
    Right
}

 

옛날 트렌드로는 각 타입 첫 글자 대문자를 붙여주었지만, 요즘은 제너릭에만 붙인다고 한다.

 

 

 

 

 

 


 

 

 

 

 

 

 

 

 

 

<타입을 집합으로 생각하자(좁은 타입과 넓은 타입)>

 

 

 

 

 

type A = string | number;
type B = string;

 

넓은 타입에서 좁은 타입으로 대입 불가능, 그 반대는 가능
비슷한 원리로 any는 전체 집합, never는 공집합으로 볼 수 있어요

 

 


객체의 경우는 좀 다르다
속성이 적을수록 넓은 타입, 속성이 많을수록(상세, 구체적일수록) 좁다

type A = {name: string};
type B = {name: number};
type C = {name: string, age: number};

 

 

 

좁은 타입, 넓은 타입 검사할 때,

객체리터럴을 바로 집어넣으면 잉여속성 검사라는 게 등장하여 안 되는 경우가 발생할 수 있다.

 

type A = {name: string};
type B = {name: number};
type AB = A | B;
TYPE C = A & B;

const ab: AB = { name: 'zerocho'};
const c: C = { name: 'zerocho', age: 29, married: false };
//  married: false 잉여속성검사 걸림

// 그래서 따로 변수로 빼서 할당해주면 안 걸림
const obj = { name: 'zerocho', age: 29, married: false }
const c: C = obj;

 

 

 

 

 

 

 


 

 

 

 

 

 

 

다음 내용(next)

void의 두 가지 사용법
unknown과 any(그리고 타입 대입가능표)
타입 좁히기(타입 가드)
커스텀 타입 가드(is, 형식 조건자)
{}와 Object
readonly, 인덱스드 시그니처, 맵드 타입스
클래스의 새로운 기능들
옵셔널, 제네릭 기본
기본값 타이핑
섹션 2 lib.es5.d.ts 분석 - forEach, map 제네릭 분석

 

 

 

 

 

 

반응형