TypeScriptのTypeGuard
Type Predicates
use typeof
code:example.ts
code:TypeScript
function func(value: string | number): number {
if ('string' === typeof value) {
// value: string
return value.length;
} else {
// value: number
return value;
}
}
use Template Literal Types
since TypeScript v4.5
code:ts
export interface Success {
type: ${string}Success;
body: string;
}
export interface Error {
type: ${string}Error;
message: string;
}
export function handler(r: Success | Error) {
if (r.type === "HttpSuccess") {
// 'r' has type 'Success'
let token = r.body;
return;
}
const a = r; // ここは Success | Errorであることに注意
}
ifの後で、Errorに絞り込まれていないことに注意
なぜなら、HttpSucccess以外のSuccessが存在しうるから
e.g. "HogeSuccess"
#WIP
https://www.typescriptlang.org/docs/handbook/2/narrowing.html
asserts x is T
const
readonly
instanceof
union
in演算子
private用のTypeScript v4.5#625bc4971982700000335af7
#hoge in obj
code:ts
type Shape =
| { kind: 'circle'; radius: number }
| { kind: 'square'; sideLength: number };
function area(shape: Shape): number {
const { kind } = shape;
if (kind === 'circle') {
return Math.PI * shape.radius ** 2;
}
return shape.sideLength ** 2;
}
伝搬できる
code:ts
function f(x: string | number | boolean) {
const isString = typeof x === 'string';
const isNumber = typeof x === 'number';
const isStringOrNumber = isString || isNumber;
if (isStringOrNumber) {
x; // 'string | number'.
} else {
x; // 'x' is 'boolean'.
}
}
https://medium.com/@OlegVaraksin/narrow-interfaces-in-typescript-5dadbce7b463
https://numb86-tech.hatenablog.com/entry/2020/06/30/154343
https://herringtondarkholme.github.io/2017/02/04/flow-sensitive/
https://retool.com/blog/typescript-control-flow-analysis-best-of/
https://javascript.plainenglish.io/typescript-4-4-be-on-your-guard-7e3bb2e3ebbe
TypeScript 4.6で起こるタグ付きユニオンのさらなる進化
分割代入後も判定できる
code:ts
type Action =
| { kind: "NumberContents", payload: number }
| { kind: "StringContents", payload: string };
function processAction(action: Action) {
const { kind, payload } = action;
if (kind === "NumberContents") {
let num = payload * 2
// ...
}
else if (kind === "StringContents") {
const str = payload.trim();
// ...
}
}
ただし、以下のように書いた場合は判定されない
code:ng.ts
const kind = action.kind
const payload = action.payload
code:ng.ts
const {kind} = action
const {payload} = action
上の例でのpayload部分は、property名が共通している必要がある
union型の絞り込み
code:ts
interface Hoge {
foo: string;
bar: number;
}
interface Piyo {
foo: number;
baz: boolean;
}
function useHogePiyo(obj: Hoge | Piyo): void {
// ここではobjはHoge | Piyo型
if ('bar' in obj) { // hasOwnPropertyを使わずともinを使える
// barプロパティがあるのはHoge型なのでここではobjはHoge型
console.log('Hoge', obj.bar);
} else {
// barプロパティがないのでここではobjはPiyo型
console.log('Piyo', obj.baz);
}
}
可変長引数...argsでも使える
https://github.com/microsoft/TypeScript/pull/47190
code:ts
type Func = (...args: "a", number | "b", string) => void;
const f1: Func = (kind, payload) => {
if (kind === "a") {
// 以前 payload :: number | string
// v4.6 payload :: number
}
if (kind === "b") {
// 以前 payload :: number | string
// v4.6 payload :: string
}
};
instanceof
instanceに対して、それが何のClassのinstanceかを判断できる
https://qiita.com/murank/items/bc1776efbecbd2120d94#instanceof-type-guards
code:ts
if (hoge instanceof Hoge) {
hoge; // 'Hoge' 型として扱われる
}
4.7
keyが変数で、obj[key]とした時にもtype guradされるようになった
code:ts
const key = 'a'
const numberOrString = Math.random() < 0.5 ? 42 : "hello";
const obj = {
'a': numberOrString,
};
if (typeof objkey === "string") {
// 以前: objkey :: string | number
// v4.7: objkey :: string
}
// 以前は変数に束縛しておく必要があった
const a = objkey
if (typeof a === "string") {
// 以前,v4.7: a :: string
}
https://medium.com/@wittydeveloper/typescript-make-types-real-the-type-guard-functions-814364e8dbe3
https://github.com/Microsoft/TypeScript/blob/master/lib/lib.dom.d.ts#L5202-L5210
https://stackoverflow.com/questions/14425568/interface-type-check-with-typescript
https://moyapro.com/2019/04/05/typescript-user-defined-type-guards/
https://blog.uhy.ooo/entry/2021-04-09/typescript-is-any-as/