項目52 オーバーロードシグネチャより条件型を優先的に使用する
オーバーロードシグネチャによる型定義の問題点
code:ts
declare function double<T extends string | number>(x: T): T;
function double(x) {return x + x;}
const num = double(12);
// ^? const num: 12
const str = double('x');
// ^? const str: "x"
上記の型定義は、文字列リテラル型を渡すと、同じ文字列リテラルが返さえる精度の低い型になっている
'x'を渡すと'xx'になる
下記のようなオーバーロードシグネチャを使って、対応した場合、ユニオン型の表現はできなくなる
オーバーロードシグネチャは型が一致するものが見つかるまで、1つずつ順に処理する
code:ts
declare function double(x: number): number;
declare function double(x: string): string;
const num = double(12);
// ^? const num: number
const str = double('x');
// ^? const str: string
条件型
いくつかの型の可能性をカバーする際に有効
code:ts
declare function double<T extends string | number>(
x: T
): T extends string ? string : number;
オーバーロードシグネチャより、条件型を優先して使用する
ユニオンの分配により、条件型をオーバーロードなしでユニオン型をサポートする宣言ができるため ただ、ユニオンを利用できそうになければ、異なる名前の関数に分けたほうがコードの可読性が高くならないか検討する
オーバーロードされた関数や条件型を返す関数を実装する際の注意点
関数本体で型アサーションを必要とするケースが有る
対策
オーバーロードと1つ定義し、関数を定義するときは異なる型シグネチャを呼び出し元に提示する
code:ts
function double<T extends string | number>(
x: T
): T extends string ? string : number;
function double(x: string | number): string | number {
return typeof x === 'string' ? x + x : x + x;
}
ただ、完璧な型と言えないので型のテストが必要