Typescript: 'ユニオンのクイックフィックスはインデックス署名では使用できません。代わりにマップされたオブジェクトタイプを使用してください'

作成日 2018年05月17日  ·  37コメント  ·  ソース: microsoft/TypeScript

次のコード:

type K = "foo" | "bar";

interface SomeType {
    [prop: K]: any;
}

このエラーメッセージを表示します:

An index signature parameter type cannot be a union type. Consider using a mapped object type instead.

マップされたオブジェクトタイプが何であるかは誰にもわからないので、簡単に修正してみましょう。

  • インデックス署名をマップされたタイプに切り替えます
  • 他のメンバーを、交差タイプと結合される別のオブジェクトタイプに移動します
  • 包含オブジェクトタイプがインターフェイスの場合、包含オブジェクトタイプをタイプエイリアスに変更します
  • 含まれているオブジェクトタイプがインターフェイスであり、 extends句がある場合、すべてのextends句と交差します
Error Messages Quick Fixes Moderate Fixed Suggestion help wanted

最も参考になるコメント

あなたはこれを行うことができます:

type Foo = 'a' | 'b';
type Bar = {[key in Foo]: any};

Barはインデックス署名がありませんが(つまり、 (obj as Bar)[value as Foo]実行することはできません)。

編集:警告を問題にしないようにすることができれば、私は永遠に感謝します!

全てのコメント37件

マップされたオブジェクトタイプが何であるかは誰にもわからないので、簡単に修正してみましょう。

+1、サンプルコードごとに2.9がインデックスシグネチャとしてユニオンをサポートすることを期待していたため、ここに来ました。 これは長い間望まれていた機能だと思います:#5683、#16760など。

あなたはこれを行うことができます:

type Foo = 'a' | 'b';
type Bar = {[key in Foo]: any};

Barはインデックス署名がありませんが(つまり、 (obj as Bar)[value as Foo]実行することはできません)。

編集:警告を問題にしないようにすることができれば、私は永遠に感謝します!

私はこれに取り組みたいです:笑い:

他のメンバーを、交差タイプと結合される別のオブジェクトタイプに移動します

オブジェクトタイプを含むことがクラスである場合はどうすればよいですか?
インターフェースだとしか想像できません

では、クイックフィックス後にコードに従う必要がありますか?

type K = "1" | "2"

class SomeType {
    a = 1;
    [prop: K]: any;
}

では、クイックフィックス後にコードに従う必要がありますか?

これは修正できないはずだと思います。

@mhegazy 3.0.0-rcを使用していますが、最初に投稿されたものと同じエラーが発生します。 これは予想されますか?

3.0.0-rcを使用していますが、最初に投稿されたものと同じエラーが発生します。 これは予想されますか?

はい。 エラーは正しいです。 この問題は、エラーメッセージの横にある軽いパルプであるそれに対するクイックフィックスの追加を追跡していました。

2.9.1およびvscodeで使用できるコードアクションはありません

@ThaJayこの機能はバックポートしません。新しいバージョンを設定してみてください。

明らかに。 最初にタイムラインをチェックしなかったことをお詫びします。十分に新しいと思いました。 tsは初めてです。 バージョン3で確認します。

これを説明する方法:

function createRequestTypes(base){
  return ['REQUEST', 'SUCCESS', 'FAILURE'].reduce((acc, type) => {
    acc[type] = `${base}_${type}`
    return acc
  }, {})
}

const user = createRequestTypes('USER')
console.log(user.REQUEST) // error
// just string? like:
interface IRequestType: {[key: string]: string}

私は以下を試しましたが、すべて失敗しました:

type requestStatus = 'REQUEST' | 'SUCCESS' | 'FAILURE'
type requestTypes = {
  [key in requestStatus]: string
}
// or
interface IRequestTypes {[key: keyType]: string}
// or even
type requestTypes = {
  FAILURE: string,
  SUCCESS: string,
  REQUEST: string
}

@maicWorkGithubここに行きます:

const user = createRequestTypes('USER')
console.log(user.REQUEST) 

function createRequestTypes(base:string):requestTypes {
  const result : requestTypes    = {}
  const arr    : requestStatus[] = ['REQUEST', 'SUCCESS', 'FAILURE']  

  return arr.reduce((acc, type) => {
    acc[type] = `${base}_${type}`
    return acc
  }, result)
}


type requestStatus = 'REQUEST' | 'SUCCESS' | 'FAILURE'
type requestTypes = { [key in requestStatus]?: string }

@ihorskyiありがとう!!

type機能する理由に興味がありますが、 interfaceは機能しません。 誰か説明してもらえますか? interfaceそのような制限(または機能?)の理由は何ですか?

type Foo = 'a' | 'b';
type Bar = {[key in Foo]: any}; // ok
interface Baz {[key in Foo]: any} // =>

// A computed property name in an interface must refer to an expression whose type is a literal type or a 'unique symbol' type.ts(1169)
// A computed property name must be of type 'string', 'number', 'symbol', or 'any'.ts(2464)
// 'Foo' only refers to a type, but is being used as a value here.ts(2693)

これは発見すべき驚くべき自動修正でした。 実装していただきありがとうございます! :)

クラスについても同じです。

あなたはこれを行うことができます:

type Foo = 'a' | 'b';
type Bar = {[key in Foo]: any};

Barはインデックス署名がありませんが(つまり、 (obj as Bar)[value as Foo]実行することはできません)。

編集:警告を問題にしないようにすることができれば、私は永遠に感謝します!

代わりにRecord使用してください!

type Foo = 'a' | 'b'
type Bar = Record<Foo, any>

クラスを使用してこの例をもう1つ追加するには...

class Foo {
   a: string;
   b: string;
}

type Bar = {[key in keyof Foo]: any};

言葉はありません、ただミーム:
https://media1.tenor.com/images/23d9d746fc87b3a93298af43dae21f6a/tenor.gif

:)

パーシャルを使用するとさらに良くなります

type A = 'x' | 'y' | 'z';
type M = Partial<{
    [key in A]: boolean
}>;

マップされたオブジェクトタイプが何であるかは誰にもわからないので、簡単に修正してみましょう。

@DanielRosenwasser
エラーメッセージが答えを示唆できないのはなぜですか。たとえば、マップされたタイプを使用した簡単な例を示します。これは、Typescriptエラーメッセージの平均の長さと一致する数行のコードのみです:trollface:

タイプまたは列挙をキーとして使用するインターフェイスが1つのプロパティのみを受け入れることができると言うことができるかどうか誰かが知っていますか?

たとえば、次のような署名: { <field>: { <and|or|xor>: <int> } }mongoビット演算子から取得されました。

export enum BitwiseOperator {
    and = "and",
    or = "or",
    xor = "xor",
}

export type BitwiseCondition = {
    [key in BitwiseOperator]?: number;
}

次に、それを使用するときに、インターフェイスによって定義された変数に1つのプロパティしかないことを検証したいと思います。

const query: BitwiseCondition = {
  and: 5,
  or: 6  // raise a ts error
};

@ b4dnewzTypescriptではできません。 回避策: https

@ b4dnewz 、プロパティが1つだけ必要な場合は、このようにしてみませんか?

export enum BitwiseOperator {
  and = "and",
  or = "or",
  xor = "xor",
}

export type BitwiseCondition = {
  operator: BitwiseOperator;
  value: number;
}

@benwinding残念ながら、返される形状はmongodbが期待するものとは異なります

@apieceofbart提案に感謝します、私はそれを調べました、インターフェースに関して少し冗長ですが、動作することができます、最終ユーザーが試してみても大したことではないので、今それを実装するかどうかは

頭痛を避けるために、mongo-operatorsの定義をできるだけシンプルにしようとしています😁将来的には適切なサポートが追加されるかもしれません

@ b4dnewz十分に公平、

おそらく、使用できる可能性のあるより単純なオプションは次のとおりです。

export type BitwiseCondition =
  | { or: number }
  | { xor: number }
  | { and: number }

それはあなたがあまり重複せずに得る最も近いものです

@ b4dnewz十分に公平、

おそらく、使用できる可能性のあるより単純なオプションは次のとおりです。

export type BitwiseCondition =
  | { or: number }
  | { xor: number }
  | { and: number }

それはあなたがあまり重複せずに得る最も近いものです

この例では、これによってエラーが発生することはありません。

const query: BitwiseCondition = {
  and: 5,
  or: 6  // raise a ts error
};

それがポイントだと思いました

@apieceofbart

この例では、これによってエラーが発生することはありません。

export type BitwiseCondition =
  | { or: number }
  | { xor: number }
  | { and: number }

const query: BitwiseCondition = {
  and: 5,
  or: 6  // doesn't raise a ts error!
};

うわー! それは奇妙です:open_mouth:私はそれを知りませんでした!

Typescriptはオブジェクトの相互に排他的な型をサポートしていないようです。 これは、ここでの言語の提案でもありました: https

それでも技術的には可能です...

このスタックオーバーフローの答えから条件付きタイプ(最も難しいタイプ)を使用してこれを達成することが可能ですが、それはかなりではありません...。

/*
 XOR boiler plate
*/
type Without<T, U> = { [P in Exclude<keyof T, keyof U>]?: never };
type XOR<T, U> = T | U extends object
  ? (Without<T, U> & U) | (Without<U, T> & T)
  : T | U;
type XOR3<S, T, U> = XOR<S, XOR<T, U>>;

// Code start
export type BitwiseCondition = XOR3<
  { or: number },
  { xor: number },
  { and: number }
>;

const query1: BitwiseCondition = {
  and: 5
};

const query: BitwiseCondition = {
  and: 5,
  or: 6 // raise a ts error
};

誰かがこれをもっときれいにすることができるなら、してください

@mvasin FWIW、これは同じ結果を達成するように見えますが、型の場合と同じように、インターフェイスの機能である必要があることに完全に同意します。

type Foo = 'a' | 'b';

type Bar = {
  [key in Foo]: any
}

interface A extends Bar { }

class Wol implements A{
  a: any;
  b: any;
}

typescript 3.5の場合、これを行う必要があるようです。

export interface DataTableState {
  columnStats: {[key in keyof DataTable]?:{}}
}

これはこれを行うための最良の方法ですか?

インデックス署名で列挙型を使用できないのはなぜですか? マップされた型はほとんど私が望むことをしますが、TypeScriptは列挙型からのすべての文字列が定義されたキーとして存在することを期待します。 私は実際にすべてのキーが存在することを主張したくありません。キーが存在する場合、それらは列挙型に存在する必要があります。

たとえば、次のタイプの場合:

type MyType = {
  [Key: 'foo' | 'bar' | 'zip']: number;
};

これは次の条件を満たす必要があります。

const x: MyType = {
  foo: 1,
  zip: 2
};

マップされたタイプに対して他のキーを未定義に設定することもできますが、キーをオプションにすることを好みますが、それらが存在する場合、値未定義に

パーシャルを使用するとさらに良くなります

type A = 'x' | 'y' | 'z';
type M = Partial<{
    [key in A]: boolean
}>;

ありがとう!
辞書に部分的に一致するタイプを定義する必要がある場合に便利です

「部分的」はレコードでも使用できます。

type Foo = 'a' | 'b';
let foo1: Record<Foo, number> = { a: 1, b: 2 };
let foo2: Partial<Record<Foo, number>> = { a: 1 };

毎月かそこらで、無意識のうちにこのGitHubページにアクセスしています。

私の最新のものは本当に単純なものです:

interface ObjectLiteral {
    [key: string | number]: any
}
export const mapToObjectLiteral = (map: Map<string|number, any>) =>
    Array.from(map).reduce((objLit, [key, value]) => {
        objLit[key] = value
        return objLit
    }, {} as ObjectLiteral)

image

上にスクロールして回避策を見つけることができますが、この問題がわずかに異なるシナリオでの日常業務で頻繁に発生するというフィードバックを提供したかっただけです。

ここに例があります:

type MapKey = string | number;
type ObjectLiteral<T extends MapKey, V = any> = {
  [P in T extends number ? string : T]: V;
};

export const mapToObjectLiteral = <T extends MapKey, V>(map: Map<T, V>) =>
  Array.from(map).reduce((objLit, [key, value]) => {
    objLit[key as keyof ObjectLiteral<T>] = value;
    return objLit;
  }, {} as ObjectLiteral<T, V>);

// how to make a better type of map ?
const m = new Map<1 | "foo", "a" | "b">();
m.set(1, "a");
m.set("foo", "b");

const o = mapToObjectLiteral(new Map(m));

console.log(o[1], o.foo); // just got an union type of every member of 'o'

https://github.com/microsoft/TypeScript/issues/24220#issuecomment -504285702

クラスを使用してこの例をもう1つ追加するには...

class Foo {
   a: string;
   b: string;
}

type Bar = {[key in keyof Foo]: any};

非常に便利。 ありがとう! 🚀

このページは役に立ちましたか?
0 / 5 - 0 評価