올 해에는 고쳐야 할 10가지 타입스크립트 버릇

참고

타입스크립트와 자바스크립트는 작년에도 꾸준히 발전해왔고, 최근 10년 동안 몇몇 습관들이 정착되었다. 몇몇은 전혀 쓸모가 없었다. 여기 우리가 고쳐야 할 10가지 습관들을 적어본다.

  1. strict 모드를 사용하지 않는 것
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// in tsconfig.json
{
"compilerOptions": {
"target": "ES2015",
"module": "commonjs"
}
}

// ↓↓↓ it should look like ↓↓↓
{
"compilerOptions": {
"target": "ES2015",
"module": "commonjs",
"strict": true
}
}
  1. 기본값들을 ||로 정의하지 않기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function createBlogPost(text: string, author: string, date?: Date) {
return {
text: text,
author: author,
date: date || new Date(),
};
}

// ↓↓↓ it should look like ↓↓↓
function createBlogPost (text: string, author: string, date: Date = new Date())
return {
text: text,
author: author,
date: date
}
}
  1. any를 사용하지 않기
1
2
3
4
5
6
7
8
9
10
11
12
async function loadProducts(): Promise<Product[]> {
const response = await fetch("https://api.mysite.com/products");
const products: any = await response.json();
return products;
}

// ↓↓↓ it should look like ↓↓↓
async function loadProducts(): Promise<Product[]> {
const response = await fetch("https://api.mysite.com/products");
const products: unknown = await response.json(); // any 대신에 unknown을 사용할 것
return products as Product[]; // 또한, as를 사용하여 리턴값을 반드시 정의하기
}
  1. val as SomeType 대신 val is SomeType을 사용하기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 하지만 unknown을 쓰기보다는 타입가드를 적용하자
async function loadProducts(): Promise<Product[]> {
const response = await fetch("https://api.mysite.com/products");
const products: unknown = await response.json();
return products as Product[];
}

// ↓↓↓ it should look like ↓↓↓
function isArrayOfProducts(obj: unknown): obj is Product[] {
return Array.isArray(obj) && obj.every(isProduct);
}

function isProduct(obj: unknown): obj is Product {
return obj != null && typeof (obj as Product).id === "string";
}

async function loadProducts(): Promise<Product[]> {
const response = await fetch("https://api.mysite.com/products");
const products: unknown = await response.json();
if (!isArrayOfProducts(products)) {
throw new TypeError("Received malformed products API response");
}
return products;
}
  • 일반 타입가드, 사용자 정의 타입가드에 관해서는 이 페이지를 참고
  1. as any를 테스트에서 사용하지 않기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
interface User {
id: string
firstName: string
lastName: string
email: string
}

test('createEmailText returns text that greats the user by first name', () => {
const user: User = {
firstName: 'John'
} as any

expect(createEmailText(user)).toContain(user.firstName)
}

// ↓↓↓ it should look like ↓↓↓
interface User {
id: string
firstName: string
lastName: string
email: string
}

class MockUser implements User {
id = 'id'
firstName = 'John'
lastName = 'Doe'
email = 'john@doe.com'
}

test('createEmailText returns text that greats the user by first name', () => {
const user = new MockUser()

expect(createEmailText(user)).toContain(user.firstName)
}
  1. 선택적 프로퍼티
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
interface Product {
id: string;
type: "digital" | "physical";
weightInKg?: number;
sizeInMb?: number;
}

// ↓↓↓ it should look like ↓↓↓
interface Product {
id: string;
type: "digital" | "physical";
}

// 선택적 프로퍼티가 적용되는 타입에 따라 객체가 각각 다른 구조를 갖는다면 상속을 활용할 것
interface DigitalProduct extends Product {
type: "digital";
sizeInMb: number;
}

interface PhysicalProduct extends Product {
type: "physical";
weightInKg: number;
}
  1. 한 글자의 제네릭을 사용하지 않기
1
2
3
4
5
6
7
8
9
// 제네릭 또한 변수명처럼 사용해야한다. 말인즉슨 알아보기 쉬워야 한다
function head<T>(arr: T[]): T | undefined {
return arr[0];
}

// ↓↓↓ it should look like ↓↓↓
function head<Element>(arr: Element[]): Element | undefined {
return arr[0];
}
  1. 자바스크립트에서 제공하는 non-boolean형 타입추론 체크 대신 자발적인 타입 체크를 사용할 것
1
2
3
4
5
6
7
8
9
10
11
12
13
14
function createNewMessagesResponse(countOfNewMessages?: number) {
if (countOfNewMessages) {
return `You have ${countOfNewMessages} new messages`;
}
return "Error: Could not retrieve number of new messages";
}

// ↓↓↓ it should look like ↓↓↓
function createNewMessagesResponse(countOfNewMessages?: number) {
if (countOfNewMessages !== undefined) {
return `You have ${countOfNewMessages} new messages`;
}
return "Error: Could not retrieve number of new messages";
}
  1. Bang Bang 연산자(!!)를 사용하지 않기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
function createNewMessagesResponse(countOfNewMessages?: number) {
if (!!countOfNewMessages) {
return `You have ${countOfNewMessages} new messages`;
}
return "Error: Could not retrieve number of new messages";
}

// ↓↓↓ it should look like ↓↓↓
function createNewMessagesResponse(countOfNewMessages?: number) {
if (countOfNewMessages !== undefined) {
return `You have ${countOfNewMessages} new messages`;
}
return "Error: Could not retrieve number of new messages";
}
  1. != null은 이제 그만
1
2
3
4
5
6
7
8
9
10
11
12
13
14
function createNewMessagesResponse(countOfNewMessages?: number) {
if (countOfNewMessages != null) {
return `You have ${countOfNewMessages} new messages`;
}
return "Error: Could not retrieve number of new messages";
}

// ↓↓↓ it should look like ↓↓↓
function createNewMessagesResponse(countOfNewMessages?: number) {
if (countOfNewMessages !== undefined) {
return `You have ${countOfNewMessages} new messages`;
}
return "Error: Could not retrieve number of new messages";
}