3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

unknown, any, never と extends の関係のメモ

Posted at

Conditional Types(X extends Y ? A : B)で
unknownany が絡んでハマってしまったので
その際に調べた内容をメモとして残しておきます。

調べたこと

X extends Y において XYany, unknown が登場したときに
どのような判定になるかを調べてみました。
あとついでにnever もいれてみた

TypeScript のバージョンは3.9.7 で検証しました。
型推論の結果確認は VSCode 上でフォーカスをあてて確認しました。

コード

判定用のコードです。

type spu1 = unknown extends string ? "" : never;
type spu2 = unknown extends number ? "" : never;
type spu3 = unknown extends unknown ? "" : never;
type spu4 = unknown extends any ? "" : never;
type spu5 = unknown extends never ? "" : never;
type spu6 = unknown extends null ? "" : never;
type spu7 = unknown extends undefined ? "" : never;
type spu8 = unknown extends symbol ? "" : never;
type spu9 = unknown extends {} ? "" : never;

type sbu1 = string extends unknown ? "" : never;
type sbu2 = number extends unknown ? "" : never;
type sbu3 = unknown extends unknown ? "" : never;
type sbu4 = any extends unknown ? "" : never;
type sbu5 = never extends unknown ? "" : never;
type sbu6 = null extends unknown ? "" : never;
type sbu7 = undefined extends unknown ? "" : never;
type sbu8 = symbol extends unknown ? "" : never;
type sbu9 = {} extends unknown ? "" : never;

//---
type spa1 = any extends string ? "" : never;
type spa2 = any extends number ? "" : never;
type spa3 = any extends unknown ? "" : never;
type spa4 = any extends any ? "" : never;
type spa5 = any extends never ? "" : never;
type spa6 = any extends null ? "" : never;
type spa7 = any extends undefined ? "" : never;
type spa8 = any extends symbol ? "" : never;
type spa9 = any extends {} ? "" : never;

type sba1 = string extends any ? "" : never;
type sba2 = number extends any ? "" : never;
type sba3 = unknown extends any ? "" : never;
type sba4 = any extends any ? "" : never;
type sba5 = never extends any ? "" : never;
type sba6 = null extends any ? "" : never;
type sba7 = undefined extends any ? "" : never;
type sba8 = symbol extends any ? "" : never;
type sba9 = {} extends any ? "" : never;

//---
type spn1 = never extends string ? "" : never;
type spn2 = never extends number ? "" : never;
type spn3 = never extends unknown ? "" : never;
type spn4 = never extends any ? "" : never;
type spn5 = never extends never ? "" : never;
type spn6 = never extends null ? "" : never;
type spn7 = never extends undefined ? "" : never;
type spn8 = never extends symbol ? "" : never;
type spn9 = never extends {} ? "" : never;

type sbn1 = string extends never ? "" : never;
type sbn2 = number extends never ? "" : never;
type sbn3 = unknown extends never ? "" : never;
type sbn4 = any extends never ? "" : never;
type sbn5 = never extends never ? "" : never;
type sbn6 = null extends never ? "" : never;
type sbn7 = undefined extends never ? "" : never;
type sbn8 = symbol extends never ? "" : never;
type sbn9 = {} extends never ? "" : never;

結果

以下のような結果になりました。

unknown

type unknown extends T T extends unknown
string
number
unknown
any
never
null
undefined
symbol
{}

any

type any extends T T extends any
string
number
unknown
any
never
null
undefined
symbol
{}

never

type never extends T T extends never
string
number
unknown
any
never
null
undefined
symbol
{}

上記のような結果になりました。

めでたしめでたし 😀

ジェネリックを使うとneverの部分で結果が変わる

上記の調査では型をハードコーディングしていますが、
最初はジェネリックを使って以下のようなコードで検証していました。

type IsSuperUnknown<T> = unknown extends T ? "" : never;
type IsSubUnknown<T> = T extends unknown ? "" : never;

type IsSuperAny<T> = any extends T ? "" : never;
type IsSubAny<T> = T extends any ? "" : never;

type IsSuperNever<T> = never extends T ? "" : never;
type IsSubNever<T> = T extends never ? "" : never;

//検証コード
type spu1 = IsSuperUnknown<string>;
type spu2 = IsSuperUnknown<number>;
//以下省略

ところがこのコードだと 以下のような現象が起きました。

//never extends unknown
type sbu5 = IsSubUnknown<never>; //never
type spn3 = IsSuperNever<unknown>; //〇

//never extends any
type sba5 = IsSubAny<never>; //never
type spn4 = IsSuperNever<any>; //〇

試しに変数に代入してみます

let vsbu5: sbu5 = ""; //コンパイルエラー
let vspn3: spn3 = "";

let vsba5: sba5 = ""; //コンパイルエラー
let vspn4: spn4 = "";

この挙動は何なんでしょう...
仕様なのかな?🤔

おわりに

可視化をすることで理解を深めることができました。
こういった検証も大事なんだなとあらためて実感しました😀

あとジェネリック使った時の結果は何なんでしょうか...

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?