[번역] TypeScript의 Do's and Don'ts
2019/02/11 22:52
TypeScript 공식 documentation의 Do's and Don'ts를 번역해 보았다.
#TIL#typscript

Do’s and Don’ts

General Types

Number, String, Boolean, and Oject

Don’t Number, String, Boolean 또는 Object 타입을 절대 사용하지 마라. 이 타입들은 JavaScript 코드에서 대부분 적절하지 않게 사용되는 non-primitive boxed object1들을 의미한다.

/* WRONG */
function reverse(s: String): String;

Do number, string 그리고 boolean을 사용하라.

/* OK */
function reverse(s: string): string;

Object 대신 non-primitive인 object 타입을 사용하라 (TypeScript 2.2에 추가됨).

Generic

Don’t 타입 매개변수를 사용하지 않는 generic 타입을 사용하지 마라. 더 자세한 내용은 TypeScript FAQ page에서 확인.2

Callback Types

Return Types of Callbacks

Don’t 값이 무시될 콜백의 리턴타입으로 any를 사용하지 마라.

/* WRONG */
function fn(x: () => any) {
  x();
}

Do 값이 무시될 콜백의 리턴타입으로 void를 사용하라.

/* OK */
function fn(x: () => void) {
  x();
}

Why 확인되지 않는 방법으로 x의 리턴타입을 사용하는 실수를 방지해주기 때문에 void를 사용하는 것이 더 안전하다.

function fn(x: () => void) {
  var k = x(); // oops! meant to do something else
  k.doSomething(); // error, but would be OK if the return type had been 'any'
}

Optional Paameters in Callbacks

Don’t 의도한 경우를 제외하고는 선택적 매개변수를 사용하지 마라.

/* WRONG */
interface Fetcher {
  getObject(done: (data: any, elapsedTime?: number) => void): void;
}

이것은 매우 분명한 의미를 갖는다. : done 콜백은 1개 또는 2개의 매개변수를 가지고 호출된다. 작성자는 아마 콜백이 elapsedTime 매개변수에 대해 신경쓰지 않는다는 것을 의도했을 것이다. 그러나 그것을 위해 선택적 매개변수로 만들 필요는 없다. - 더 적은 매개변수를 받는 콜백을 전달하는 것은 언제나 적합하다.3

Do 콜백의 파라미터를 필수(non-optional) 파라미터로 작성하라.

/* OK */
interface Fetcher {
  getObject(done: (data: any, elapsedTime: number) => void): void;
}

Overloads and Callbacks

Don’t 콜백만 다른 별도의 타입 시그니처(overload)를 작성하지 마라.

/* WRONG */
declare function beforeAll(action: () => void, timeout?: number): void;
declare function beforeAll(
  action: (done: DoneFn) => void,
  timeout?: number
): void;

Do 모든 것을 포함하는 타입 시그니처 하나만 작성하라.

/* OK */
declare function beforeAll(
  action: (done: DoneFn) => void,
  timeout?: number
): void;

Why 콜백에서 매개변수를 무시하는 것은 언제나 적합하다.(역자:3과 같은 의미이다.) 따라서 더 간단한 타입 시그니처는 필요하지 않다. 간단한 타입 시그니처를 제공하는 것은 타입이 잘못된 함수가 첫번째 함수 시그니처와 매칭되어 전달되는 것을 허용한다.4

Function Overloads

Ordering

Don’t 더 구체적인 시그니처 전에 일반적인 시그니처를 두지 마라.

/* WRONG */
declare function fn(x: any): any;
declare function fn(x: HTMLElement): number;
declare function fn(x: HTMLDivElement): string;

var myElem: HTMLDivElement;
var x = fn(myElem); // x: any, wat?

Do 더 구체적인 시그니처 다음에 일반적인 시그니처를 두도록 정렬하라.

/* OK */
declare function fn(x: HTMLDivElement): string;
declare function fn(x: HTMLElement): number;
declare function fn(x: any): any;

var myElem: HTMLDivElement;
var x = fn(myElem); // x: string, :)

Why TypeScript는 함수 호출을 결정할 때 처음 매칭되는 시그니처를 서낵한다. 먼저 나오는 시그니처가 뒤의 시그니처보다 더 일반적일 경우, 뒤에 나오는 것은 완벽히 가려지고 호출될 수 없다.

Use Optional Parameters

Don’t 뒤에 나오는 매개변수들만 다른 경우 여러 시그니처를 작성하지 마라.

/* WRONG */
interface Example {
  diff(one: string): number;
  diff(one: string, two: string): number;
  diff(one: string, two: string, three: boolean): number;
}

Do 가능한 경우에는 선택적 매개변수를 사용하라.

/* OK */
interface Example {
  diff(one: string, two?: string, three?: boolean): number;
}

이 방법은 모든 시그니처의 리턴타입이 같은 경우에만 가능하다는 것을 주의하라.

Why 여기엔 두 가지 중요한 이유가 있다.

TypeScript는 source의 매개변수로 target의 시그니처 호출할 수 있는지, 그리고 관련없는 매개변수가 허용되는지를 보고 시그니처 호환성을 결정한다. 예를 들어, 이 코드는 시그니처가 선택적 매개변수를 사용하여 정확히 작성된 경우에만 버그를 발생시킨다.

function fn(x: (a: string, b: number, c: number) => void) {}
var x: Example;
// 오버로딩으로 작성되어 있으면, OK -- 첫 번째 시그니처를 사용
// 선택적 매개변수로 작성되어 있으면, 에러
fn(x.diff);

두 번째 이유는 TypeScript의 stric null checking 기능을 사용하는 경우이다. JavaScript에서 명시되지 않은 매개변수는 undefined로 표현되기 때문에, 일반적으로 선택적 매개변수를 가진 함수에 undefined를 명시적으로 전달하는 것이 좋다. 예를 들어 이 코드는 strict null을 사용하는 경우에도 문제없다.

var x: Example;
// 오버로딩으로 작성되어 있으면, `undefined`를 `string` 타입으로 전달하기 때문에 에러
// 선택적 매개변수로 작성되어 있으면, 정상
x.diff('something', true ? undefined : 'hour');

Use Union Types

Don’t 오직 하나의 매개변수 타입이 다른 경우에는 오버로딩을 사용하지 마라.

/* WRONG */
interface Moment {
  utcOffset(): number;
  utcOffset(b: number): Moment;
  utcOffset(b: string): Moment;
}

Do 가능한 경우에는 유니온 타입을 사용하라.

/* OK */
interface Moment {
  utcOffset(): number;
  utcOffset(b: number | string): Moment;
}

시그니처의 리턴타입이 다르기 때문에 b를 선택적 매개변수로 하지 않은 것을 주의하라.

Why 이것은 함수에 값을 그대로 전달하는 사람들에게 중요하다.

function fn(x: string): void;
function fn(x: number): void;
function fn(x: number | string) {
  // 오버로딩으로 작성되어 있으면, 에러
  // 유니온 타입으로 작성되어 있으면, 정상
  return moment().utcOffset(x);
}

  1. primitive타입이 아닌 boxing된 object형 타입. 따라서 Numer.MAX_VALUE나 Number.isSafeInteger()등으로 사용가능하다.

    ↩
  2. generic으로 선언하고 내부에서 사용하지 않는 경우를 만들지 말라는 의미로, FAQ 페이지를 보면 해당 타입 매개변수를 사용하는 부분이 없으면 타입추론이 제대로 이루어지지 않는다고 한다.

    ↩
  3. 매개변수의 개수에 대한 함수의 타입 호환성에서 할당받는 함수의 매개변수가 더 많은 경우는 호환이 가능하기 때문이다. 자주 사용하는 map(),filter() 같은 함수에 매개변수 하나만 사용하는 콜백을 넘기는 경우를 생각하면 쉽다.

    ↩
  4. 호출하는 쪽에서는 항상 정해진 수의 매개변수를 전달하고 콜백에서 필요한 만큼만 받아서 사용해야 하는데, 매개변수 개수가 적은 시그니처가 있으면 호출하는 쪽에서 매개변수를 적게 전달하는 실수가 허용된다.

    ↩