引数に応じて戻り値の型は変化しない #
例えば以下の関数と実行結果があるとして、age
の型は何になるでしょうか?
const getAge = (shouldString: boolean) => {
return shouldString ? "20" : 20;
};
const age = getAge(true);
引数に true
を渡しているため age
が文字列としての '20'
であることは明白です。
ただし、残念ながら型はそこまで推論できず age
の型は 20 | "20"
になってしまいます。
Conditional Types を使うだけだとダメ #
Conditional Types を利用すればうまくいきそうですが、残念ながらこれだけだとうまくいきません。
const getAge = <T extends boolean>(
shouldString: T,
): T extends true ? "20" : 20 => {
return shouldString ? "20" : 20;
};
return
文で返そうとしている型と、戻り値の型として定義している内容が一致しないというエラーが出てしまいます。
「Conditional Types + アサーション」が必要 #
Conditional Types に加えてアサーションを記載することでようやく実現できます。
const getAge = <T extends boolean>(
shouldString: T,
): T extends true ? "20" : 20 => {
return (shouldString ? "20" : 20) as T extends true ? "20" : 20;
};
const age = getAge(true);
これでようやく age
の型は '20'
になります。
また可読性を考慮すると、以下のように書くのが良いのではないでしょうか。
type Age<T> = T extends true ? "20" : 20;
const getAge = <T extends boolean>(shouldString: T): Age<T> => {
return (shouldString ? "20" : 20) as Age<T>;
};
参考:色々なアサーションの記述方法 #
ここまでで内容は以上ですが、以下では参考として、アサーションの書き方で考えられるすべてのパターンを書いておきます。お好みの記述方法を使用してください。
as でアサーション #
const getAge = <T extends boolean>(
shouldString: T,
): T extends true ? "20" : 20 => {
return shouldString
? ("20" as T extends true ? "20" : 20)
: (20 as T extends true ? "20" : 20);
};
const getAge = <T extends boolean>(
shouldString: T,
): T extends true ? "20" : 20 => {
return (shouldString ? "20" : 20) as T extends true ? "20" : 20;
};
type Age<T> = T extends true ? "20" : 20;
const getAge = <T extends boolean>(shouldString: T): Age<T> => {
return shouldString ? ("20" as Age<T>) : (20 as Age<T>);
};
type Age<T> = T extends true ? "20" : 20;
const getAge = <T extends boolean>(shouldString: T): Age<T> => {
return (shouldString ? "20" : 20) as Age<T>;
};
<> でアサーション #
const getAge = <T extends boolean>(
shouldString: T,
): T extends true ? "20" : 20 => {
return shouldString
? <T extends true ? "20" : 20>"20"
: <T extends true ? "20" : 20>20;
};
const getAge = <T extends boolean>(
shouldString: T,
): T extends true ? "20" : 20 => {
return <T extends true ? "20" : 20>(shouldString ? "20" : 20);
};
type Age<T> = T extends true ? "20" : 20;
const getAge = <T extends boolean>(shouldString: T): Age<T> => {
return shouldString ? <Age<T>>"20" : <Age<T>>20;
};
type Age<T> = T extends true ? "20" : 20;
const getAge = <T extends boolean>(shouldString: T): Age<T> => {
return <Age<T>>(shouldString ? "20" : 20);
};