前提・実現したいこと
TypeScript で, 以下のアルゴリズムと型制約を満たす twice 関数のソースコードが欲しい
アルゴリズム
[x] 引数の型が number または string でない場合,エラーオブジェクト(実行時エラー)を返すこと(number, string 以外の型を引数で受け取らないようにするotherwiseに相当するもの)
追記(DR;TL): 今回の質問の本質は「引数の型制約と引数と返り値の一致」なので,条件から削除する. ~~元々,関数型言語のパターンマッチングのように,想定外の型の条件分岐ができないようにすること,想定した型の条件分岐を強制的に書かせることの2つを満たすやり方をTypeScript で探していた. 一番目をdefaultとErrorの返却で満たせるのでは,と考えて条件に追加していた. 2つ目は,やり方が分からないので条件に入れていない ~~- 数字(number)の場合は, その数字の2倍の値を返すこと
- 文字列(string)の場合は, その文字列を2回繰り返した文字列を返すこと
型制約
- 引数の型が number または string でない場合, 型エラー(コンパイルエラー)が発生すること
- 引数と返り値の型が一致しない場合, 型エラー(コンパイルエラー)が発生すること (引数が T で返り値が T 以外の場合)
発生している問題・エラーメッセージ
Type 'number' is not assignable to type 'T'. 'number' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'Twicable'.(2322) Type 'string' is not assignable to type 'T'. 'string' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'Twicable'.(2322)
該当のソースコード
twice関数 例1 | TypeSctipt Playground
TypeScript
1function assertNever(x: any): never { 2 throw new Error("Unexpected object: " + x); 3} 4 5type Twiceable = number | string; 6 7function twice<T extends Twiceable>(x: T): T { 8 switch (typeof x) { 9 case 'number': 10 return x * 2; // should not be type error 11 // return 'hello' + 'hello'; // should be type error 12 case 'string': 13 return x + x; // should not be type error 14 // return 2 * 2; // should be type error 15 default: 16 return assertNever(x); 17 } 18} 19 20console.log(twice(2)); 21console.log(twice('hello')); 22console.log(twice(true)); // should be type error
試したこと
返り値を Twiceable に変更した. 上述の返り値のエラーは出なくなるが, 型制約の二番目
を満たせない.
twice関数 例2 | TypeSctipt Playground
typesctipt
1function assertNever(x: any): never { 2 throw new Error("Unexpected object: " + x); 3} 4 5type Twiceable = number | string; 6 7// NOTE: change T of return value to Twicable 8function twice<T extends Twiceable>(x: T): Twiceable { 9 switch (typeof x) { 10 case 'number': 11 return 'hello' + 'hello'; // NOTE: should be error 12 case 'string': 13 return 2 * 2; // NOTE: should be error 14 default: 15 return assertNever(x); 16 } 17} 18 19console.log(twice(2)); 20console.log(twice('hello')); 21console.log(twice(true)); // should be error
typescript
1type Twiceable = number | string; 2 3function twice<T>(x: T): T { 4 switch (typeof x) { 5 case 'number': 6 return x * 2; // should not be type error 7 // return 'hello' + 'hello'; // should be type error 8 case 'string': 9 return x + x; // should not be type error 10 // return 2 * 2; // should be type error 11 12 } 13 return never; 14} 15 16console.log(twice<number>(2)); 17console.log(twice<string>('hello')); 18console.log(twice(true)); // should be type error
補足情報(FW/ツールのバージョンなど)
「// should be error」はコンパイルエラーでしょうか、実行時エラーでしょうか?そして、どのようなエラーが起きるべきところでしょうか。
// shoud be error の部分は,コンパイル時やエディタ等での赤線として,実行前に出るコンパイルエラーを想定しています.
例外として,アルゴリズムの一番目のエラーオブジェクトの部分のみ実行時エラーを想定しています
具体的には,引数の型と返り値の型が異なる場合,以下のようなコンパイルエラーメッセージが出ることを期待しています.
```
// Type 'string' is not assignable to type 'number'.(2322)
case 'number':
return 'hello' + 'hello';
// Type 'number' is not assignable to type 'string'.(2322)
case 'string':
return 2 * 2;
```
同様に,指定外の引数の型の場合は,以下です
```
Argument of type 'true' is not assignable to parameter of type 'Twiceable'.(2345)
console.log(twice(true));
```