ジェネリック型からリターン型を推測する

0
2022.01.14

特定の状況で型を推論して、入力パラメータに基づいて Box<T> または Tを取得したいのですが、代わりに Box<unknown> と不明になります。 私はW extends Wrap<T>の使い方のせいだと思いますが、これをより良く表現する方法がわかりません。

次のコード例を参照し 、boxResultnoBoxResult にカーソルを合わせると、推論された型が私にとって理想的でないことがわかります。

// Type definitions

interface ComplicatedDataContainer<T> {
    // contains many Ts in some complicated fashion
}
interface WrapA<T> {
    v: ComplicatedDataContainer<T>,
    p: "Box"
}
interface WrapB<T> {
    v: ComplicatedDataContainer<T>,
    p: "NoBox"
}
type Wrap<T> = WrapA<T> | WrapB<T>;

type Box<T> = {
    t: T
}

type SelectThing = <T, W extends Wrap<T>>(id: string, w: W) => W extends {p: "Box"} ? Box<T> : T

// Usage example

const wBox: WrapA<string> = {
    v: "",
    p: "Box"
}

const wNoBox: WrapB<string> = {
    v: "",
    p: "NoBox"
}

const selector: SelectThing = (id: string, w: Wrap<any>) => {throw new Error()}; // dont care about runtime behavior here, real impls are way more complicated

// I want the type of this to be Box<string>, but it is Box<unknown>
const boxResult = selector("", wBox);
// I want the type of this to be string, but it is unknown
const noBoxResult = selector("", wNoBox);

この例は 、ここで実行できます。

回答
4
2022.01.14

問題は 、T の推定サイトがないため、デフォルトで unknownが使用されるということです。実際には、型パラメータ T から派生できるため、ここで別の型パラメータ Wは必要ありません。以下の Unwrap 型は、この派生を行います。

type Unwrap<W extends Wrap<any>> = W extends Wrap<infer T> ? T : never

type SelectThing =
    <W extends Wrap<any>>(id: string, w: W) => W extends {p: "Box"} ? Box<Unwrap<W>> : Unwrap<W>

プレイグラウンドリンク

意図しない構造的等価性を避けるために、何らかの方法でTに依存するように、例のComplicatedDataContainerを記入しなければならなかったことに注意してください。