[번역] 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_VALUENumber.isSafeInteger()등으로 사용가능하다.

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

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

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