86580


以下のような感じでプロパティ名にワイルドカードをつけて型付けをする方法はあるのでしょうか。

※以下のコードはエラーがでます

[code]

    type Image = {
      url*: string;
    　[key: string]: string;
    }
    
    const images: Image[] = [{
      url123: ["path/to/a", "path/to/b"], // 裏表のある画像へのパスになっている
      ...
    }, {
      url456: ["path/to/a", "path/to/b"],
      ...
    }, {
      url789: ["path/to/a", "path/to/b"],
      ...
    }]
    
[/code]

## 追記 この質問をした経緯

業務上で既存のjsファイルをtsに移行する作業を行っています。

あるコード内で取得しに行っている、あるAPIが

[code]

    // 実際のコードから質問用に改変しているので不備があるかもしれません
    // url + id
    [{
      url123: ["path/to/a", "path/to/b"], // 裏表のある画像へのパスになっています
      ...
    }, {
      url456: ["path/to/a", "path/to/b"],
      ...
    }, {
      url789: ["path/to/a", "path/to/b"],
      ...
    }]
    
[/code]

のようなオブジェクトの配列を返しています。

この配列に対して

[code]

    const images: Image[] = fetchImages();
    images[0][`url${id}`][0] .......
    
[/code]

のような感じで、 `images[0][`url${id}`][0]`
の部分のようにプロパティの中の要素に対してインデックスでアクセスしている箇所がありそこで、`型 '0' の式を使用して型 'Image'
にインデックスを付けることはできないため、要素は暗黙的に 'any' 型になります。プロパティ '0' は型 'Image'
に存在しません。ts(7053)`というtsエラーが出ました。  
そのため、imagesオブジェクトをインデックスシグネチャのみで型付けすると出来なかったために上記のような質問をさせていただきました。


CC BY-SA 4.0
2022-02-25T09:48:01.730
86572
2022-02-26T13:48:27.630
2022-02-26T13:48:27.630
50855
50855
question
typescript
オブジェクトの型付けの際にプロパティ名にワイルドカードのようなものを使用できるか

[code]

    type Image = {
      [key: `url${number}`]: string[];
      // ...
    };};
    
[/code]

でどうでしょうか。  
(参考: [Template Literal
Types](https://www.typescriptlang.org/docs/handbook/2/template-literal-
types.html))

[code]

    type Image = {
      [key: `url${number}`]: string[];
      hoge1: string;
      hoge2: string;
      // ...
    };
    
    const images: Image[] = [
      {
        url123: ["path/to/a", "path/to/b"], // 裏表のある画像へのパスになっています
        hoge1: "hoge1",
        hoge2: "hoge2",
        // ...
      },
      {
        url456: ["path/to/a", "path/to/b"],
        hoge1: "hoge1",
        hoge2: "hoge2",
        // ...
      },
      {
        url789: ["path/to/a", "path/to/b"],
        hoge1: "hoge1",
        hoge2: "hoge2",
        // ...
      },
    ];
    
    const id = 123;
    console.log(images[0][`url${id}`][0]); // "path/to/a"
    
[/code]

([Playground](https://www.typescriptlang.org/play?#code/C4TwDgpgBAkgtgQwObQLxQN4CgpQNoDWEIAXFAAYCuATgDYAkGAdpXAEYTUC+5AumQGdg1AJZMkeXgG4cUABYB7FAEZBwsUhm5FKAExrR4rVAD0JqADorWLjKwBjBUyFQRiFALLxkESVHR4sti4uDS0yroAzGR4AERgCMByJsAKJgixADRQ8YnJqSZssbzZZlCA8xGAFhGAdgyAQgyA0QyA3K6A8wqAHgzVgIsMgJ0MgNYMgFYMgMYMgGYMgCIMgH4MgJoMstpKEKo5OnNZ0-Kz+gtryyGm5lYWslyZQSthACwArABsMblJKWkZ2bf5aUUlK4vzsZ9bIYvr302R22ZT2B2BmBOdAA7AAOACcNwSdwKjxyyJehWKEJmKjIgJUv1xEAB-
yJO0s1lwhyw0iwDicLhEABN-
FAIpEZI5nApaBALLQlAAKNw+AR4AAMvDwVDojBZPGlUoAlFIKc97ulYlggA))


CC BY-SA 4.0
2022-02-26T00:57:14.080
86577
2022-02-26T00:57:14.080
2808
86572
answer


私の知る限り、TypeScript 4.5 までで、今回の問題に簡潔に対処できる方法はありません。可能なのであれば API
が返すレスポンスのスキーマを変更する方が利便性はあるでしょう。その上で、部分的に解決できる方法を考えました。

## コード量が少なく、おおよそ賄える方法

[Template Literal
Types](https://www.typescriptlang.org/docs/handbook/2/template-literal-
types.html) を用いると、今回の用法をおおよそ賄えます。Template Literal Types とは String Literal
Types において更にテンプレート文字列のような記法を用いることができるという機能です。

具体的には、以下のように書けます。

[code]

    type ImageURLKey = `url${number}`;
    
    type Image = {
      [key: ImageURLKey]: string[];
    }
    
    const data: Image[] = [
      {
        url123: ["path/to/a", "path/to/b"],
      },
      {
        url456: ["path/to/a", "path/to/b"],
      },
      {
        url789: ["path/to/a", "path/to/b"],
      },
    ];
    
[/code]

ここにデータとして `{ piyo123: [] }` などを足すと型検査が通らないことが確認できます。

ただし、この書き方には欠点があります。Template Literal Types の `${number}` は JavaScript が
`number` としてパースできる文字列を大体通してしまいます。このため `url1e2` や `url0x1`、`url 123`
なども型検査に通ってしまいます。特に、`url-123` が型検査を通ってしまうので、うっかり kebab-case
だと勘違いしてしまっても気付けません。このことを許容できるのであれば、分かりやすい型の付け方と思います。

TypeScript 側でも正整数のみを受け付ける型というのが検討はされているようなので、そこに貢献していくというのも手です。

参考：

  * <https://github.com/microsoft/TypeScript/issues/46109>
  * <https://github.com/microsoft/TypeScript/issues/46674>
  * <https://stackoverflow.com/q/66294091/5989200>

## 真面目に「数字の列」の型を作る方法

上記の方法は、JavaScript の `number` を使っているために欠点が生まれるのでした。それでは `number`
を作るのではなくて、単に「数字が並んでいる文字列」という型を作って解決できないでしょうか。

もし ID の上限値が決まっているのであれば、Template Literal Types を使うことで以下のように書けます。

[code]

    type NonZeroDigitString = `${1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9}`;
    type DigitString = '0' | NonZeroDigitString;
    type MaybeDigitString = DigitString | '';
    type ImageIDLessThanThousand = '0' | `${NonZeroDigitString}${MaybeDigitString}${MaybeDigitString}`
    
    type Image = Partial<Record<`url${ImageIDLessThanThousand}`, string[]>>
    
[/code]

ただし ID がとても大きくなる場合には型の情報も大きくなってしまいますし、あまりオススメはできません。

## なんとか上限を突破する

上限を設定しない型も、[Recursive Conditional
Types](https://www.typescriptlang.org/docs/handbook/release-
notes/typescript-4-1.html#recursive-conditional-types)
を使うと実装できる……はずです。具体的には以下のような型を考えました。

[code]

    type NonZeroDigitString = `${1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9}`;
    type DigitString = '0' | NonZeroDigitString;
    type ImageID<T extends string, V = T> = T extends DigitString ? V :
      T extends `${DigitString}${infer R}` ? ImageID<R, V> : never;
    type ImageURLKey<T extends string, V = T> = T extends `url${infer R}` ? ImageID<R, V> : never;
    type Image<T extends string> = Record<ImageURLKey<T>, string[]>
    
[/code]

ただしこの型は、計算に時間がかかるのか、手元で試すと時々期待通りに動いてくれないので、あんまり使いものにはならなさそうでした（おそらく……）。


CC BY-SA 4.0
2022-02-26T02:52:38.563
86580
2022-02-26T02:52:38.563
19110
86572
answer