【TypeScript / satisfies】型注釈(type annotation) はもういらない?
■はじめに
TypeScript v4.9
から利用可能になったsatisfies
Operator
利用していくうちに
satisfies
を使えば型注釈 (type annotation)
は必要ないのでは?
という考えが浮かび記事にしてみました。
型注釈 (type annotation) 参考資料
■結論
ほとんどのケースで、
型注釈 (type annotation)
ではなく、satisfies
を利用した方がよいと考えています。
satisfies
の機能・動作の説明とともに後述していきます。
■satisfies とは
ざっくり
型推論をいい感じに効かせながら型を限定できる機能
と理解しました。
■satisfies の良いところを動作とともに確認していく
satisfies の良いところは下記2つを実現できるところにあります。
- 型を限定できる
- 型推論をいい感じに効かせることができる
最初下記のように定義したとします。
type Color = {
// Mapped Typesで key を限定
[key in 'red' | 'blue' | 'green']: string | number[]
}
const colorsSatisfies= {
red: '#ff0000',
blue: '#0000ff',
green: [0, 255, 0],
} satisfies Color;
1. 型を限定できる
型チェックが効いてくれる
const colorsSatisfies= {
red: '#ff0000',
blue: '#0000ff',
green: [0, 255, 0],
// NG: 許容しない型を定義しようとするとエラー
yellow: [255, 255, 0],
} satisfies Color;
2. 型推論を効かせることができる
型推論が効くので、下記型になります。
red: string
blue: string
green: number[]
// NG: string は map を持っていないためエラー
colorsSatisfies.red.map((v) => v)
// OK
colorsSatisfies.green.map((v) => v)
■satisfies を利用しない場合
型を限定したい場合ほとんどの場合型注釈 (type annotation)
を利用すると思います。
type Color = {
// Mapped Typesで key を限定
[key in 'red' | 'blue' | 'green']: string | number[]
}
const colorsAnnotation: Color = {
red: '#ff0000',
blue: '#0000ff',
green: [0, 255, 0],
}
◯型注釈でも型は限定できる
型チェックが効いてくれる
const colorsAnnotation: Color = {
red: '#ff0000',
blue: '#0000ff',
green: [0, 255, 0],
// NG: yellow を許容しないためエラー
yellow: [255, 255, 0],
}
◯型注釈ではいい感じに型推論を効かせることができない
型注釈 (type annotation)
の場合、型推論がいい感じに効いてくれません。
下記の型になる。
red: string | number[]
blue: string | number[]
green: string | number[]
// NG: string のため当然エラー
colorsAnnotation.red.map((v) => v)
// NG: string の可能性もあるためエラー
colorsAnnotation.green.map((v) => v)
このパターンでmap関数
を利用したい場合、型ガードを利用すればできます。
(なかなか、しんどい実装...)
if (typeof colorsAnnotation.green !== 'string') {
// OK
colorsAnnotation.green.map((v) => v)
}
■型注釈はもう必要ないのか?
satisfies
は型を限定できるかつ型推論をいい感じに効かせることができるので、
型注釈 (type annotation)
もういらないのでは? と思えてきます。
ただ、
型が推論できない場面ではsatisfies
を利用することはできないです。
type UnionColor = 'red' | 'blue' | 'green'
// NG
let colors satisfies UnionColor
// OK
let colors: UnionColor
つまり、推論すべき値が存在しない場面では
satisfies
を利用できず、
型注釈
を利用するしかないです。
型が推論できない場面 を除いてはsatisfies
を使った方がいいと思います。
最後に さらに便利なsatisfies
の使い方を紹介していきます。
as const
とsatisfies
を一緒に使うと便利
■satisfies と as const を組み合わせて使うと、3つの恩恵を享受できます。
-
satisfies
により型を限定することができる -
as const
により値が widening されない -
as const
により readonly の値にできる
TypeScript の Widening
※ widening の例: '#ff0000'型 -> string型
に広がって推論されてしまう
satisfies
により 型を限定することができる
1. type Color = {
// Mapped Typesで key を限定
[key in 'red' | 'blue' | 'green']: string | readonly number[]
}
const colorsAsConstSatisfies = {
red: '#ff0000',
blue: '#0000ff',
green: [0, 255, 0],
// NG: yellow を許容しないためエラー 型を限定することができる
yellow: [255, 255, 0],
} as const satisfies Color;
as const
だけだと型を限定できない
const colors = {
red: '#ff0000',
blue: '#0000ff',
green: [0, 255, 0],
// キーを限定できない yellow を許容する
yellow: [255, 255, 0],
} as const;
as const
により値が widening されない
2. type Color = {
// Mapped Typesで key を限定
[key in 'red' | 'blue' | 'green']: string | readonly number[]
}
const colorsAsConstSatisfies = {
red: '#ff0000',
blue: '#0000ff',
green: [0, 255, 0],
} as const satisfies Color;
型推論の結果
// widening されない
const colorsAsConstSatisfies = {
readonly red: '#ff0000';
readonly blue: '#0000ff';
readonly green: readonly [0, 255, 0];
}
satisfies
だけだと widening されてしまう
const colorsSatisfies = {
red: '#ff0000',
blue: '#0000ff',
green: [0, 255, 0],
} satisfies Color;
型推論の結果
// widening されてしまう
const colorsSatisfies = {
red: string;
blue: string;
green: number[];
}
as const
により readonly の値にできる
3. type Color = {
// Mapped Typesで key を限定
[key in 'red' | 'blue' | 'green']: string | readonly number[]
}
const colorsAsConstSatisfies = {
red: '#ff0000',
blue: '#0000ff',
green: [0, 255, 0],
} as const satisfies Color;
red
に代入しようとすると
// NG: readonly のためエラー
colorsAsConstSatisfies.red = 'red'
satisfies
だけだと readonly の値にならない
red
に代入しようとすると
const colorsSatisfies = {
red: '#ff0000',
blue: '#0000ff',
green: [0, 255, 0],
} satisfies Color;
// OK: readonly ではないため
colorsSatisfies.red = 'red'
■最後に
読んでいただきありがとうございます!
ご指摘、意見などあれば気軽にコメントお待ちしております!
参考資料
ありがとうございました!
- https://zenn.dev/moneyforward/articles/typescript-as-const-satisfies#satisfies-なし、-as-const-ありの場合
- https://qiita.com/suin/items/1b74645158263d2fa9af
- https://zenn.dev/luvmini511/articles/55ad71c1ae99ba#3.-satisfies-で解決できる問題
- https://zenn.dev/estra/articles/typescript-widening
- https://typescriptbook.jp/reference/values-types-variables/object/type-annotation-of-objects
Discussion