質問編集履歴

3 追記

siic

siic score 31

2020/07/23 08:22  投稿

引数の型によって特定の型の返り値(戻り値)を返す関数(入力された型で出力される型が決まる関数)の作り方
型ArgAか型ArgBを受け取って、受け取った型に応じてRetAかRetBの型を返す関数を例とします。
```typescript
type ArgA = [string, number, boolean]
type ArgB = [number, number]
type RetA = {abc: number}
type RetB = {vw: string, x: {y: number, z: number}}
function sample<T extends ArgA | ArgB>(arg: T): RetA | RetB{
 if (typeof arg[0] == 'string') { //ArgA
   return {abc: 1} //RetA
 }
 return {vw: 'foo', x: {y: 222, z: 333}} //RetB
}
let foo = sample(['a', 2, false]) //RetA に違いないが RetA | RetB
let bar = sample([5, 10]) //RetB に違いないが RetA | RetB
```
戻り値の型は `RetA | RetB` ですが、明らかに入力時点で出力される型の予想はつくので、TypeScriptに出力まで推測してもらおうと思い、関数宣言部分に戻り値の型 `Result<T>` を指定しました。
```typescript
type Result<T extends ArgA | ArgB> = T extends ArgA ? RetA : RetB
function sample<T extends ArgA | ArgB>(arg: T): Result<T> {
 if (typeof arg[0] == 'string') {
   return {abc: 1} //甲
 }
 return {vw: 'foo', x: {y: 222, z: 333}} //乙
}
```
そうするとreturn の部分(甲、乙)で「型'‥' を 'Result<T>' に割り当てることができません。(ts2322) 」とvscodeのLSPがエラーを表示しました。
以下のようにas型アノテーションをつけることでエラーを抑えました。
```
function sample<T extends ArgA | ArgB>(arg: T): Result<T> {
 if (typeof arg[0] == 'string') {
   return {abc: 1} as Result<T>
 }
 return {vw: 'foo', x: {y: 222, z: 333}} as Result<T>
}
```
型アノテーションを使うのは釈然としませんが、現状このような解決方法しかないのでしょうか?
もっといい方法があればご教授ください。
___
(質問2)
また、関数の型をオーバーロードを使って定義するように書き換えようとしたところ、うまくいきませんでした。
<*追記:オーバーロードは使わないことにしました。>
```typescript
type ArgA = [string, number, boolean]
type ArgB = [number, number]
type RetA = {abc: number}
type RetB = {vw: string, x: {y: number, z: number}}
type Result<T extends ArgA | ArgB> = T extends ArgA ? RetA : RetB
type Sample = {
 (arg: ArgA): RetA
 (arg: ArgB): RetB
}
const sample2: Sample = <T extends ArgA | ArgB>(arg: T)=>{ //<-Error
 if (typeof arg[0] == 'string') {
   return {abc: 1} as Result<T>
 }
 return {vw: 'foo', x: {y: 222, z: 333}} as Result<T>
}
```
vscodeのLSPエラー内容
```
型 '<T extends ArgA | ArgB>(arg: T) => Result<T>' を型 'Sample' に割り当てることはできません。
 型 'RetA | RetB' を型 'RetA' に割り当てることはできません。
   プロパティ 'abc' は型 'RetB' にありませんが、型 'RetA' では必須です。ts(2322)
```
これはどうしてでしょうか。オーバーロードを使って、「型に応じて返す型が決まる関数」の定義方法があるのなら教えていただけませんか?
これはどうしてでしょうか。~~オーバーロードを使って、「型に応じて返す型が決まる関数」の定義方法があるのなら教えていただけませんか?~~ オーバーロードは仕様を表現するだけで型を仕分けするのはコーダーの仕事のようです。
  • TypeScript

    1340 questions

    TypeScriptは、マイクロソフトによって開発された フリーでオープンソースのプログラミング言語です。 TypeScriptは、JavaScriptの構文の拡張であるので、既存の JavaScriptのコードにわずかな修正を加えれば動作します。

2 質問2の解決

siic

siic score 31

2020/07/23 08:20  投稿

引数の型によって特定の型の返り値(戻り値)を返す関数(入力された型で出力される型が決まる関数)の作り方
型ArgAか型ArgBを受け取って、受け取った型に応じてRetAかRetBの型を返す関数を例とします。
```typescript
type ArgA = [string, number, boolean]
type ArgB = [number, number]
type RetA = {abc: number}
type RetB = {vw: string, x: {y: number, z: number}}
function sample<T extends ArgA | ArgB>(arg: T): RetA | RetB{
 if (typeof arg[0] == 'string') { //ArgA
   return {abc: 1} //RetA
 }
 return {vw: 'foo', x: {y: 222, z: 333}} //RetB
}
let foo = sample(['a', 2, false]) //RetA に違いないが RetA | RetB
let bar = sample([5, 10]) //RetB に違いないが RetA | RetB
```
戻り値の型は `RetA | RetB` ですが、明らかに入力時点で出力される型の予想はつくので、TypeScriptに出力まで推測してもらおうと思い、関数宣言部分に戻り値の型 `Result<T>` を指定しました。
```typescript
type Result<T extends ArgA | ArgB> = T extends ArgA ? RetA : RetB
function sample<T extends ArgA | ArgB>(arg: T): Result<T> {
 if (typeof arg[0] == 'string') {
   return {abc: 1} //甲
 }
 return {vw: 'foo', x: {y: 222, z: 333}} //乙
}
```
そうするとreturn の部分(甲、乙)で「型'‥' を 'Result<T>' に割り当てることができません。(ts2322) 」とvscodeのLSPがエラーを表示しました。
以下のようにas型アノテーションをつけることでエラーを抑えました。
```
function sample<T extends ArgA | ArgB>(arg: T): Result<T> {
 if (typeof arg[0] == 'string') {
   return {abc: 1} as Result<T>
 }
 return {vw: 'foo', x: {y: 222, z: 333}} as Result<T>
}
```
型アノテーションを使うのは釈然としませんが、現状このような解決方法しかないのでしょうか?
もっといい方法があればご教授ください。
___
(質問2)
また、関数の型をオーバーロードを使って定義するように書き換えようとしたところ、うまくいきませんでした。
<*追記:オーバーロードは使わないことにしました。>  
```typescript
type ArgA = [string, number, boolean]
type ArgB = [number, number]
type RetA = {abc: number}
type RetB = {vw: string, x: {y: number, z: number}}
type Result<T extends ArgA | ArgB> = T extends ArgA ? RetA : RetB
type Sample = {
 (arg: ArgA): RetA
 (arg: ArgB): RetB
}
const sample2: Sample = <T extends ArgA | ArgB>(arg: T)=>{ //<-Error
 if (typeof arg[0] == 'string') {
   return {abc: 1} as Result<T>
 }
 return {vw: 'foo', x: {y: 222, z: 333}} as Result<T>
}
```
vscodeのLSPエラー内容
```
型 '<T extends ArgA | ArgB>(arg: T) => Result<T>' を型 'Sample' に割り当てることはできません。
 型 'RetA | RetB' を型 'RetA' に割り当てることはできません。
   プロパティ 'abc' は型 'RetB' にありませんが、型 'RetA' では必須です。ts(2322)
```
これはどうしてでしょうか。オーバーロードを使って、「型に応じて返す型が決まる関数」の定義方法があるのなら教えていただけませんか?
  • TypeScript

    1340 questions

    TypeScriptは、マイクロソフトによって開発された フリーでオープンソースのプログラミング言語です。 TypeScriptは、JavaScriptの構文の拡張であるので、既存の JavaScriptのコードにわずかな修正を加えれば動作します。

1 <hr>追加

siic

siic score 31

2020/07/22 20:16  投稿

引数の型によって特定の型の返り値(戻り値)を返す関数(入力された型で出力される型が決まる関数)の作り方
型ArgAか型ArgBを受け取って、受け取った型に応じてRetAかRetBの型を返す関数を例とします。
```typescript
type ArgA = [string, number, boolean]
type ArgB = [number, number]
type RetA = {abc: number}
type RetB = {vw: string, x: {y: number, z: number}}
function sample<T extends ArgA | ArgB>(arg: T): RetA | RetB{
 if (typeof arg[0] == 'string') { //ArgA
   return {abc: 1} //RetA
 }
 return {vw: 'foo', x: {y: 222, z: 333}} //RetB
}
let foo = sample(['a', 2, false]) //RetA に違いないが RetA | RetB
let bar = sample([5, 10]) //RetB に違いないが RetA | RetB
```
戻り値の型は `RetA | RetB` ですが、明らかに入力時点で出力される型の予想はつくので、TypeScriptに出力まで推測してもらおうと思い、関数宣言部分に戻り値の型 `Result<T>` を指定しました。
```typescript
type Result<T extends ArgA | ArgB> = T extends ArgA ? RetA : RetB
function sample<T extends ArgA | ArgB>(arg: T): Result<T> {
 if (typeof arg[0] == 'string') {
   return {abc: 1} //甲
 }
 return {vw: 'foo', x: {y: 222, z: 333}} //乙
}
```
そうするとreturn の部分(甲、乙)で「型'‥' を 'Result<T>' に割り当てることができません。(ts2322) 」とvscodeのLSPがエラーを表示しました。
以下のようにas型アノテーションをつけることでエラーを抑えました。
```
function sample<T extends ArgA | ArgB>(arg: T): Result<T> {
 if (typeof arg[0] == 'string') {
   return {abc: 1} as Result<T>
 }
 return {vw: 'foo', x: {y: 222, z: 333}} as Result<T>
}
```
型アノテーションを使うのは釈然としませんが、現状このような解決方法しかないのでしょうか?
もっといい方法があればご教授ください。
___
(質問2)
また、関数の型をオーバーロードを使って定義するように書き換えようとしたところ、うまくいきませんでした。
```typescript
type ArgA = [string, number, boolean]
type ArgB = [number, number]
type RetA = {abc: number}
type RetB = {vw: string, x: {y: number, z: number}}
type Result<T extends ArgA | ArgB> = T extends ArgA ? RetA : RetB
type Sample = {
 (arg: ArgA): RetA
 (arg: ArgB): RetB
}
const sample2: Sample = <T extends ArgA | ArgB>(arg: T)=>{ //<-Error
 if (typeof arg[0] == 'string') {
   return {abc: 1} as Result<T>
 }
 return {vw: 'foo', x: {y: 222, z: 333}} as Result<T>
}
```
vscodeのLSPエラー内容
```
型 '<T extends ArgA | ArgB>(arg: T) => Result<T>' を型 'Sample' に割り当てることはできません。
 型 'RetA | RetB' を型 'RetA' に割り当てることはできません。
   プロパティ 'abc' は型 'RetB' にありませんが、型 'RetA' では必須です。ts(2322)
```
これはどうしてでしょうか。オーバーロードを使って、「型に応じて返す型が決まる関数」の定義方法があるのなら教えていただけませんか?
  • TypeScript

    1340 questions

    TypeScriptは、マイクロソフトによって開発された フリーでオープンソースのプログラミング言語です。 TypeScriptは、JavaScriptの構文の拡張であるので、既存の JavaScriptのコードにわずかな修正を加えれば動作します。

思考するエンジニアのためのQ&Aサイト「teratail」について詳しく知る