세미나 일자: 2020-08-26
발표 공고가 나왔을 때부터 두근두근하며 기다렸다.
타입스크립트를 사용하고는 있지만, 잘 사용하는지, 어떻게 하면 더 잘 사용할 수 있을지, 다른 회사들은 어떻게 쓰고 있는지가 항상 궁금했었다.
이번 세미나가 그런 갈증을 조금이나마 해소해줄 수 있었던 것 같다.
영상은 며칠 내로 유트브에 공개된다고 한다. 발표자료를 토대로 필요한 내용만 정리해보자.
타입 시스템
- 컴파일러에게 사용하는 타입을 명시적으로 지정하는 시스템
- 컴파일러가 자동으로 타입을 추론하는 시스템
타입을 사용하면서 타입 시스템의 정의가 뭔지 깊게 생각해본 적이 없었다.
단지 타입을 강제해서 오류를 줄일 수 있다는 것만 막연하게 생각했었다.
그래서 이런 정의가 신선하면서도 명쾌하게 느껴졌다.
noImplicitAny옵션을 켜면타입을 명시적으로 저장하지 않은 경우
타입스크립트가 추론 중
any라고 판단하게 되면컴파일 에러를 발생시켜
명시적으로 지정하도록 유도한다.
이번 세미나에서 얻은 소득 중 첫 번째가 tsconfig의 명쾌한 설명이다.
막연하게 알고 있었던 항목들을 분명하게 이해하게 됐다.
strictNullChecks옵션을 켜면모든 타입에 자동으로 포함되어 있는
null과undefined를 제거해준다.
noImplicitReturns옵션을 켜면함수 내엣 모든 코드가 값을 리턴하지 않으면,
컴파일 에러를 발생시킨다.
모든 코드에서 명시적으로 리턴 타입을 지정하자!
structural type system - 구조가 같은면 같은 타입이다.
nominal type system - 구조가 같아도 이름이 다르면 다른 타입이다.
타입스크립트의 타입 시스템은 structural type system이다.
하지만 nominal type system을 사용하고자 하면 구현할 수 있다.
import { find } from 'lodash';
type PersonId = string & { readonly brand: unique symbol };
function PersonId(id: string): PersonId {
return id as PersonId;
}
interface Person {
id: PersonId;
name: string;
}
function getPersonById(id: PersonId) {
const data: Array<Person> = [
{
id: PersonId('aaa'),
name: 'aaa',
},
{
id: PersonId('bbb'),
name: 'bbb',
},
{
id: PersonId('ccc'),
name: 'ccc',
},
];
return find(data, p => p.id === id);
}
console.log(getPersonById(PersonId('bbb')));
console.log(getPersonById('bbb'));발표자료에 있던 코드를 거의 그대로 들고왔다.
PersonId는 단순 string이지만 unique symbol로 intersection을 걸어서 만들어졌기 때문에 string을 그대로 전달할 수 없다.
명확하게 이해는 되지만 어디에 써먹을 수 있을까 알쏭달쏭하다.
이렇게나 강하게 타입을 강제해야하는 상황이 있을까?
// type alias
type EatType = (food: string) => void;
// interface
interface Eat {
(food: string): void
}// type alias
type PersonList = string[];
// interface
interface PersionList {
[index: number]: string;
}type은 단순히 alias라고 생각해도 좋다고 발표자님이 얘기하신 것이 기억난다.
function, array를 interface에서 사용하는 법은 종종 잊어먹는다. 잘 기억해두자.
interface ErrorHandling {
success: boolean;
error?: { message: string };
}
interface ArtistsData {
artists: { name: string }[];
}
// type alias
type ArtistsResponseType = ArtistsData & ErrorHandling;
let art: ArtistsResponseType;
// interface
interface ArtistRespose extends ArtistsData, ErrorHandling {}
let art: ArtistsResponse;type은 에러와 데이터를 intersection으로 엮어서 새로운 타입을 만들었다.
interface는 extends를 이용하여 두 에러와 데이터를 엮었다.
이건 잘 써먹을 수 있을 것 같다!
interface를 동일한 이름으로 선언하면 각 프로퍼티들은 하나의 interface에서 선언한 것 처럼 합쳐진다.
라이브러리에서 타일을 부실하게 적어놨을 경우 활용할 수 있다.
type은 중복 선언되면 에러가 발생한다.
function getNumber(value: number | string): number {
if (typeof value === 'number') {
return value;
}
return -1;
}class NegativeNumberError extends Error {}
function getNumber(value: number): number | NegativeNumberError {
if (value < 0) return new NegativeNumberError();
return value;
}
function main() {
const num = getNumber(-10);
if (num instanceof NegativeNumberError) {
return;
}
}interface Admin {
id: string;
role: string;
}
interface User {
id: string;
email: string;
}
function redirect(user: Admin | User) {
if ("role" in user) {
routeToAdminPage(user.role);
} else {
routeToHomePage(user.email);
}
}type Car = {
type: 'CAR';
wheel: number;
}
type Boat = {
type: 'BOAT';
motor: number;
}
function getWheelOrMotor(machine: any): number {
if (machine.type === 'CAR') {
return machine.wheel;
} else if (machine.type === 'BOAT') {
return machine.motor;
} else {
return -1;
}
}
const carData: Car = {
type: 'CAR',
wheel: 5,
}
console.log(getWheelOrMotor(carData));type Car = {
type: 'CAR';
wheel: number;
}
type Boat = {
type: 'BOAT';
motor: number;
}
function isCar(arg: any): arg is Car {
return arg.type === 'CAR';
}
function isBoat(arg: any): arg is Boat {
return arg.type === 'BOAT';
}
function getWheelOrMotor(machine: any): number {
if (isCar(machine)) {
return machine.wheel;
} else if (isBoat(machine)) {
return machine.motor;
} else {
return -1;
}
}
const carData: Car = {
type: 'CAR',
wheel: 5,
}
console.log(getWheelOrMotor(carData));Type Guard는 어떻게 엮어서 사용하던지 매우 유용하게 사용할 수 있을 듯 하다.
어렴풋하게 알고 있었던 내용을 정리할 수 있어서 좋았고, 부가 설명에 적힌대로 에러 처리를 할 때 유용하게 쓸 수 있을 것 같다!
Class Property의 타입을 명시적으로 지정해야 한다.
런타임에 undefined가 된다.
strictPropertyInitalization
Class Property가 선언에서 초기화되어야 한다. or Class property가 생성자에서 초기화한다.
4.0.2부터 생성자에 의해 Class Property의 타입이 추론된다.
생성자를 벗어나면 추론되지 않는다.
!로 의도를 표현해야 한다. - 주의하라는 표시라고 이해해도 좋다.
요건 간단하게만 정리!
1부 내용만 일단 간단하게 정리해봤다. 사용된 코드는 발표 자료에 있는 코드를 거의 그대로 사용했다.
2부는 실전 타입스크립트 코드 작성하기 인데 1부보다 훨씬 더 어려웠다. 그래도 내 수준에서도 유용한 내용이 많아서 꼭 다시 리뷰해보려고 한다.
오늘은 여기까지만!
정리한 내용에 오류가 있을 수도 있으니 혹시라도 읽는 분이 있다면 유튜브나 원본 슬라이드를 참고하세요.