質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

TypeScript

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

Q&A

解決済

3回答

3283閲覧

【TypeScript】前方が可変長の配列の型定義がうまく書けない

kbash

総合スコア10

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

TypeScript

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

0グッド

1クリップ

投稿2020/09/13 07:12

TypeScript で、以下のように、「最低一個、いくつかの string の末尾一つだけが number 」という形の配列に対するTypeがうまく書けず困っております。

Typescript

1 2// 最低 1つ以上の文字列が連続し、末尾は必ず number という例 3 4const a = ['hoge', 10] 5const b = ['hoge', 'fuga', 22] 6const c = ['hoge', 'fuga', 'piyo', 35] 7

この逆で、先頭だけ number で、後方が可変長な string の場合は、 以下のようなタプル型でマッチさせることができました。

Typescript

1 2 3type T1 = [number, ...string[]] 4 5const x: T1 = [10, 'hoge',] // OK 6const y: T1 = [22, 'hoge', 'fuga' ] // OK 7const z: T1 = [35, 'hoge', 'fuga', 'piyo' ] // OK 8

試しに 以下のようにT2[...string[], number] と書くと、解釈が (string | number)[] に変化してしまい、予想外の型である、以下の de も受け入れてしまいます。

Typescript

1 2type T2 = [...string[], number] // (string | number)[] に変化してしまう。 3 4const a: T2 = ['hoge', 10] // OK 5const b: T2 = ['hoge', 'fuga', 22] // OK 6const c: T2 = ['hoge', 'fuga', 'piyo', 35] // OK 7const d: T2 = [12, 'fuga', 'piyo', 35] // OK : エラーにしたいが、受け入れてしまう 8const e: T2 = ['hoge', 12, 22, 35] // OK : エラーにしたいが、受け入れてしまう 9

また同時に以下の警告が出ます。

A rest element must be last in a tuple type.ts(1256)

この T2 をどのように書けばうまくいくでしょうか。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

miyabi_takatsuk

2020/09/13 08:32 編集

そもそも、前方に可変長を置けることは何か調べた結果でしょうか? それとも、質問者さんが、できるべと思ってやってみただけでしょうか?
kbash

2020/09/13 10:26

もともとあった古い Javascript を TypeScript化 する中で、この問題に直面しました。 影響範囲が分かりづらいため、できればデータ構造をリファクタすることなく、方の定義で書く方法はないかと思い質問を投稿致しました。
guest

回答3

0

こんにちは
ベストアンサー決定後に失礼します。

私もまだ深く追い切れていませんが、今年6月中旬にPRされ、すでにマージされている Variadic tuple types によって、

type T2 = [...string[], number] // (string | number)[] に変化してしまう。

と書けるようになったようです。ただ、これが

// (string | number)[] に変化してしまう。

のは、このPRで説明されている(現)仕様であるようです。このPRに関しては、uhyoさん@qiita の下記の投稿がとても分かりやすいです。

以下は、上記の投稿の「Variadic Tuple Typesでできないこと 」からの引用です。

同様に、[...string[], number]のようなものもサポートされません。

… このようなものはTypeScriptでサポートされませんので、解決の際に...string[]の後ろのnumberが...string[]に吸収されて...(string | number)[]となります。

この“吸収”という動作はPRの説明にも記載されており、バグではなく仕様通りの動作です。意図と異なる結果となるため戸惑う方が多そうですが、前述のようにTypeScriptではタプル型の最後以外の位置での可変長要素を(少なくとも今の段階では)サポートしませんから、やむを得ない挙動であると考えられます。

参考になれば幸いです。

備考

私の手元の確認ですと、TSのバージョン ^4.0.2  を使用して、

typescript

1type T2 = [...string[], number]

と書くと、ご質問と同じように、この行で、TS1256: A rest element must be last in a tuple type. のエラーになりましたが、 string[] を別のtypeにして

typescript

1type A = string[] 2type T2 = [...A, number]

と書いた場合は、エラーになりませんでした。そして、上記のQiitaの投稿にあるとおり、"吸収" によって、(string | number)[] となっていることを、

typescript

1const ary: T2 = [1,'str', 1, 1, 'str', 'str']

がエラーにならないことによって確認できました。

投稿2020/09/13 11:33

編集2020/09/13 15:52
jun68ykt

総合スコア9058

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

前方が可変長の配列の型定義

TypeScript でとして定義は難しいのではないでしょうか。

JavaScriptにおいては、「引数確認用の関数」というような実装は可能です。
関数内で複製した配列を破壊的に操作する実装で、似たような検査はできます。

  1. 参照を切ったArray の複製
  2. 複製を pop()

javascript

1// checkingArguments 2function test () { 3 var args = [].slice.call(arguments); // slice()で参照を切る 4 var num = args.pop(); // args の最後から「決め打ちされた数」だけ取り出す 5 var len = args.length; 6 for( var idx=0; idx<len; ++idx ) { 7 var data = args[idx]; 8 if( "string" !== typeof data ) { 9 throw TypeError(`required string in argument. ${data} #${idx}`); 10 } 11 } 12 13 console.log( args ) 14 console.log( num ) 15} 16test('hoge', 10); 17test('hoge', 'fuga', 22); 18test('hoge', 'fuga', 'piyo', 35); 19test(12, 'fuga', 'piyo', 35); 20// => Uncaught TypeError: required string in argument. 12 #0 21test('hoge', 12, 22, 35); 22// => Uncaught TypeError: required string in argument. 12 #1 23 24 25/* 26 スプレッド構文を使いたい場合 27function test (...args) { 28 var num = args.pop(); // args の最後の要素を取り出す 29 var len = args.length; 30 : 31} 32 33var a = ['hoge', 10]; 34test(...a); 35console.log( "a : ", a ); // ['hoge', 10] 36 */ 37

可能ではありますが、pop() の一手間や、配列を破壊的に操作するために、詳細な検査を開発者側で実装する必要もあります。

スプレッド構文を使う場合、 miyabi_takatsuk さんの回答通りと思います。

投稿2020/09/13 09:33

AkitoshiManabe

総合スコア5434

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kbash

2020/09/13 10:30

コード例ありがとうございます。 TypeScript側では少し緩めに定義しておいて、テスト側でエラーチェックをしていくのはよさそうですね。 :)
guest

0

ベストアンサー

言語によってできる出来ないはありますが、
TypeScriptにおいては、
可変長を前方に置くことは出来ないためと思われます。
なぜなら、前方におくと、どこまでが可変長かどうかの判別がつかないからです。
後方であれば、固定個数以降は、可変長であると判別がされます。

また、可変長は、引数で考えた時、
残余引数とも呼ばれるため、
その名称からも、前方に置くことは出来ない事が示唆せれます。

投稿2020/09/13 08:39

編集2020/09/13 10:35
miyabi_takatsuk

総合スコア9555

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

maisumakun

2020/09/13 08:51

Rubyなど言語によっては、可変長引数が途中に来ても構わない、という例はあります(固定された引数を埋めてから残りを詰める、という形で動く)。
miyabi_takatsuk

2020/09/13 10:33

maisumakunさん > ありがとうございます! 言語によって違うのですね・・・。 回答に追記していきます。
kbash

2020/09/13 10:33

「前方にはおけない」という前提で別の解決策を実施したいと思います。ありがとうございます。 > maisumakun 言語によっては可能なのですね。情報ありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問