Skip to content

Instantly share code, notes, and snippets.

@jet2jet
Last active Jan 21, 2023
Embed
What would you like to do?
// 「typeof 演算子」の型を取得するためのダミー関数
function _dummy() { return typeof ''; }
type TypeofType = ReturnType<typeof _dummy>;
// 「TypeofType」は TS 4.9 時点では「'string' | 'number' | 'bigint' | 'boolean' | 'symbol' | 'undefined' | 'object' | 'function'」になる
// typeof 演算子の結果の文字列に対応する型を書いたマップオブジェクト
interface TypeofTypeMap {
number: number;
string: string;
boolean: boolean;
bigint: bigint;
object: object | null;
function: Function;
symbol: symbol;
undefined: undefined;
}
// o オブジェクトにあるフィールド name の typeof の結果が typeName になるかをチェックする関数
// ※ もし TypeofType に更新があった場合、「TypeofTypeMap[TypeofName]」が型エラーになるので
// 「TypeofTypeMap」のマップを更新する必要がある
function isEqualFieldType<
T extends object,
K extends keyof T,
TypeofName extends TypeofType
>(o: T, name: K, typeName: TypeofName): o is T & Record<K, TypeofTypeMap[TypeofName]> {
return typeof o[name] === typeName;
}
// o オブジェクトにあるフィールド name が predicate を満たす場合、そのフィールドを O 型とみなす関数
function isEqualFieldTypeObject<
T extends object,
K extends keyof T,
O extends object
>(o: T, name: K, predicate: (x: unknown) => x is O): o is T & Record<K, O> {
return predicate(o[name]);
}
interface Foo {
foo: string;
}
interface Hoge {
hoge: number;
piyo: Foo;
}
// 子オブジェクトのチェックを関数に分離
function isFoo(o: unknown): o is Foo {
if (
typeof o !== 'object' || o == null ||
!('foo' in o) || !isEqualFieldType(o, 'foo', 'string')
) {
return false;
}
o satisfies Foo;
return true;
}
function isHoge(o: unknown): o is Hoge {
if (
typeof o !== 'object' || o == null ||
!('hoge' in o) || !isEqualFieldType(o, 'hoge', 'number') ||
// 「o.piyo」が存在して「Foo」かどうかをチェック
!('piyo' in o) || !isEqualFieldTypeObject(o, 'piyo', isFoo)
) {
return false;
}
o.piyo satisfies Foo; // 問題なし
o satisfies Hoge; // 問題なし
return true;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment