これは面白い課題ですね。
結論から言ってしまうと、ジェネリクスのAは解決しておらず、条件付き型(extends)同士が割り当て可能になるには、「extends直後の型が同値でなければならない」という性質を利用しているようです。
詳細に説明するために、一旦下記のように分解してみます。下記xがyに代入可能なら、Equalsは1を返すということになります。
declare let x: <A>() => A extends A2 ? 1 : 0
declare let y: <B>() => B extends A1 ? 1 : 0
x = y
クレジットにあるIssueのコメントで次のように理由が述べられていました:
Here's a solution that makes creative use of the assignability rule for conditional types, which requires that the types after extends be "identical" as that is defined by the checker:
つまり、上記のコードで言えば、xのextendsの直後の型A2と、yのextendsの直後の型A1が同一の場合のみ、xがyに代入可能になるようにしているようです。
具体的に、A1がany、A2がstringであるケースを考えてみます:
playground
declare let x: <A>() => A extends string ? 1 : 0
declare let y: <B>() => B extends any ? 1 : 0
// エラー: 代入できない
// Type '<B>() => B extends any ? 1 : 0' is not assignable to type '<A>() => A extends string ? 1 : 0'.
// Type 'A extends any ? 1 : 0' is not assignable to type 'A extends string ? 1 : 0'.
// Type '0 | 1' is not assignable to type 'A extends string ? 1 : 0'.
// Type '0' is not assignable to type 'A extends string ? 1 : 0'.(2322)
x = y
A1とA2がともにstringであるケースでは:
playground
declare let x: <A>() => A extends string ? 1 : 0
declare let y: <B>() => B extends string ? 1 : 0
// 代入可能
x = y
ではなぜ条件付き型(extends)同士が割り当て可能になるには、「extends直後の型が同値でなければならない」という性質があるのでしょうか。
extends直後の型が同値でなくても、条件付き型同士が割り当て可能だと仮定してみます:
declare let x: <A>() => A extends string ? 1 : 0
declare let y: <B>() => B extends number ? 1 : 0
// 1
const x_1 = x<string>()
// 0
const y_1 = y<string>()
x = y
// xの関数シグネチャの定義によれば、x_2は`1`のはず
// しかし、xにyを代入できており、y_1から`0`なので
// 戻り値は `1 | 0` のユニオン型でなければ矛盾する
const x_2 = x<string>()
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2021/11/24 01:53 編集