TypeScript에서 type predicate를 안전하게 사용하는 방법

10448 단어 TypeScript

TL;DR

import { isT, isNotT, defineIsT } from "safe-type-predicate";

// isA: (x: "a" | "b") => x is "a"
const isA = defineIsT((x: "a" | "b") =>
    x === "a" ? isT(x) : isNotT()
);

GitHub


safe-type-predicate

typepredicate 및 그 문제점


TypeScript에는 type predicate 기능이 있습니다.
이것은 적당한 유형의 값x을 받아들여 boolean으로 되돌아오는 함수의 되돌아오는 값을 x is T로 작성함으로써 되돌아오는 경우true가 되돌아오는 것이 아니라x형, T는 그렇지 않다는 것을 나타낸다.
이로써 사용자 정의 함수로 형 보호를 할 수 있다.
예를 들어, 반환false형, 즉 임의의 형을 받아들이고 그 값이 unknown형인 함수는 다음과 같다.
function isString(x: unknown): x is string {
  return typeof x === "string";
}
이것은 편리한 기능이지만 매우 큰 문제가 존재한다.그것은 금형 검사가 거의 작용하지 않는 것이다.
예를 들어 아래의 코드는 실제로는 string형의 함수인지 아닌지를 판정하지만 금형에서는 number형의 함수가 된다.
function isString(x: unknown): x is string {
  return typeof x === "number";
}
이 정도의 단순한 예라도 유형 검사를 하지 않기 때문에 약간의 오류가 오류의 원인이 되기 쉽다.
따라서 이 글은 typepredicate를 안전하게 사용하는 방법과 이를 라이브러리화하는 방법string을 소개했다.

typepredicate 안전한 사용 방법


typepredicate의 안전 처리는 safe-type-predicate를 안전하게 정의하는 함수입니다.
물론 TypeScript이기 때문에 완전히 안전하기는 어렵습니다.따라서 일정한 작법만 따르면 안전을 목표로 한다.
그리고 이를 실현하기 위해 형 방어와 형 추론을 잘 활용하는 것을 고려했다.
그리고 다음과 같은 표현을 쓸 수 있었으면 좋겠어요.
const isHoge = defineIsT((x: /* 引数に取る型 */) =>
    /* 条件式 */ ? isT(x) : isNotT()
);
이렇게 쓰면 조건식(x: T) => x is R의 시각isT(x)이 형보호에 의해 축소되기 때문에 형추리를 할 수 있다.
또한 값(즉, JS에 대한 컴파일 결과)을 고려하여 x 항등함수, defineIsTisT 각각isNotT, true 상수함수로 간단하게 할 수 있다.
남은 것은 단지 잘 정형화되었을 뿐이다.
그것으로 만든 것은 아래와 같다.
declare const isTSymbol: unique symbol;
declare const isNotTSymbol: unique symbol;

export type isT<T> = true & { _T: T; _Tag: typeof isTSymbol };
export type IsNotT = false & { _Tag: typeof isNotTSymbol };

export function isT<T>(_x: T): isT<T> {
  return true as isT<T>;
}

export function isNotT(): IsNotT {
  return false as IsNotT;
}

export function defineIsT<T, R extends T>(
  f: (x: T) => isT<R> | IsNotT
): (x: T) => x is R {
  return f as any;
}
falsenew type과 유령형의 기교를 사용하고, IsT<T>new type의 기교를 사용한다.
new type은 원래 타입으로 바꿀 수 있지만 원래 타입으로 보면 배우가 없으면 바꿀 수 없는 타입입니다.이것 없이도 동작할 수 있지만 IsNotT, isT 의 반환값 이외의 값이 들어오는 것을 방지할 수 있기 때문에 더욱 안전하다.여기는isNotT& { _Tag: typeof isTSymbol }의 부분입니다.
유령형은 유형에 따라 서로 다른 유형의 정보를 남기는 기교이다.여기에는 & { _Tag: typeof isNotTSymbol }의 부분에 & { _T: T }의 유형 정보를 남긴다.
이것을 사용하면 다음과 같은 내용을 쓸 수 있다.
// isString: (x: unknown) => x is string
const isString = defineIsT((x: unknown) =>
    typeof x === "string" ? isT(x) : isNotT()
);
그리고 여기에 쓰인 사연은 T 이라는 이름으로 라이브러리화되어 공개됐다.

사용자 정의 문자열 규칙


사용safe-type-predicate을 통해 아무것도 하지 않는 것보다 typepredicate를 안전하게 사용할 수 있습니다.
그러나 다음과 같은 기법을 사용하면 당연형 시스템과 동작이 모순된다.
// isString: (x: unknown) => x is string
const isString = defineIsT((x: unknown) =>
    isT("x")
);
'일정한 작법만 따르면 안전하다'는 목표로 만들어진 라이브러리이기 때문이다.
따라서'일정한 작법'을 강제하면 되지 않겠느냐는 점을 고려해 tslint 규칙을 제정해 safe-type-predicate의 이름으로 공개했다.tslint-safe-type-predicate, npm i -D tslint-safe-type-predicatetslint.json에 추가extends만 하면 사용할 수 있다.
예를 들어 위의 예에서 다음과 같은 경고를 보냅니다.

좋은 웹페이지 즐겨찾기