hasId(v: unknown): v is { id: unknown } { if (typeof v !== "object" || v === null) { return false; } return "id" in v; } function hasName(v: unknown): v is { name: unknown } { if (typeof v !== "object" || v === null) { return false; } return "name" in v; } function isUser(v: unknown): v is User { if (!hasId(v) || !hasName(v)) { return false; } if (typeof v.id !== "number") { return false; } if (typeof v.name !== "string") { return false; } return true; }
assertId(v: unknown): asserts v is { id: unknown } { if (typeof v !== "object" || v === null) { throw new Error("Value should be non-null object"); } if (!("id" in v)) { throw new Error('Value should have the property "id"'); } // noop } function assertName(v: unknown): asserts v is { name: unknown } { if (typeof v !== "object" || v === null) { throw new Error("Value should be non-null object"); } if (!("name" in v)) { throw new Error('Value should have the property "name"'); } // noop } function assertUser(v: unknown): asserts v is User { assertId(v); assertName(v); if (typeof v.id !== "number") { throw new Error('"id" should be a number'); } if (typeof v.name !== "string") { throw new Error('"name" should be a string'); } // noop }
is Record<string, unknown> { if (!isObject(v)) { throw new Error(`${target} should be object`.trim()); } } function isMatchedType<T extends object>( v: unknown, props: UnionToUnionTuple<keyof RemoveOptionalProperties<T>>, errorPropsRef: string[], target = "" ): v is T { assertObject(v, target); if (new Set(props).size !== props.length) { throw new Error("Invalid props"); } return props .map((prop) => { if (typeof prop !== "string") { throw new Error("Invalid prop"); } const within = prop in v; if (!within) { errorPropsRef.push(prop); // mutate } return within; }) .every((flag) => flag); } export function assertMatchedType<T extends object>( v: unknown, props: UnionToUnionTuple<keyof RemoveOptionalProperties<T>>, target = "" ): asserts v is T { const errorPropsRef: string[] = []; // ࢠʹΛ֨ೲͤ͞ΔͨΊͷۭྻࢀর w ศརؔΛassertObject()Λࣄલʹ ࡞͓ͬͯ͘ w ϓϩύςΟࢦఆͯ͢ϢχʔΫͰ͋Δ͜ͱΛ อূ w ͻͱͭͻͱͭinΛͬͯ֬ೝ w ͳ͚ΕΤϥʔࢀরʹه ϥϯλΠϜଆͷΈ
} Λ * { b: number; d: any } ͚ͩʹ͢Δɻ * * AnyAs0<T> ͕ͳ͍ͱ ?: any ͷରԠ͕࿙ΕΔͷͰҙɻ */ type RemoveOptionalProperties<T> = { [P in keyof RemoveNeverProperties< UndefinedAsNever<OptionalAsUndefined<UnknownAs0<AnyAs0<T>>>> >]: T[P]; }; function isObject(v: unknown): v is Record<string, unknown> { if (typeof v !== "object") { return false; } return v !== null; } function assertObject( v: unknown, target = "" ): asserts v is Record<string, unknown> { if (!isObject(v)) { throw new Error(`${target} should be object`.trim()); } } function isMatchedType<T extends object>( v: unknown, props: UnionToUnionTuple<keyof RemoveOptionalProperties<T>>, errorPropsRef: string[], target = "" ): v is T { assertObject(v, target); if (new Set(props).size !== props.length) { throw new Error("Invalid props"); } return props .map((prop) => { w ศརؔassertObject() "TTFSUJPOGVODUJPOTͷ ͓खຊͷΑ ͏ͳؔ assertObject()