TypeScript で、特定の型であることが明確なのに推論が効かないことがあります。
type Foo = { value: number };
type TopFooValueType<A extends Foo[]> = A extends [infer F, ...infer _] ? F['value'] : never;
// Type '"value"' cannot be used to index type 'F'.ts(2536)
// F は Foo 型のはずなのにエラーになる。
このようなケースでは、 下記のようにして制約を満たしていることを保証するかもしれません。
type TopFooValueType<A extends Foo[]> = A extends [infer F, ...infer _]
? F extends Foo
? F['value']
: never
: never;
もしくは、再利用できるように次の Must
のような型を用意することもできます。
type Must<T, ToBe> = T extends ToBe ? T : never;
type TopFooValueType<A extends Foo[]> = A extends [infer F, ...infer _] ? Must<F, Foo>['value'] : never;
この Must
、よく見ると Extract
と同じです。
type Extract<T, U> = T extends U ? T : never;
つまり、冒頭の TopFooValueType
は次のように書くことができます。
type TopFooValueType<A extends Foo[]> = A extends [infer F, ...infer _] ? Extract<F, Foo>['value'] : never;
type T = TopFooValueType<[{ value: 1 }, { value: 2 }]>; // 1
もし、型の指定が間違っている場合は never
になります(never
に対しての Lookup は常に never
であるため)。
type Bar = { value: string };
type TopFooValueType<A extends Foo[]> = A extends [infer F, ...infer _] ? Extract<F, Bar>['value'] : never;
type T = TopFooValueType<[{ value: 1 }, { value: 2 }]>; // never