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] )。

编辑:虽然如果您可以使警告无效,我将永远感激不已!

我想从事这个:laughing:

将其他成员移动到与交集类型组合在一起的单独对象类型

如果包含对象类型是类,该怎么办?
我只能想象这是一个接口

所以在quickfix之后应该遵循什么代码?

type K = "1" | "2"

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

所以在quickfix之后应该遵循什么代码?

我会说这不应该解决。

@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>

要使用类再添加一个示例...

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

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

使用Partial更好

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

没人知道映射对象的类型是什么,所以让我们对其进行快速修复:

@DanielRosenwasser
为什么错误消息不能给出答案,例如显示一个使用映射类型的快速示例-只能是几行代码,与Typescript错误消息的平均长度一致:trollface:

有谁知道是否可以说使用类型枚举作为键的接口只能接受一个属性?

例如这样的签名: { <field>: { <and|or|xor>: <int> } }来自mongo bitwise运算符

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

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

然后使用它时,我想验证由接口定义的变量是否只有一个属性。

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

@ b4dnewz您不能在Typescript中执行此操作。 解决方法: https

@ b4dnewz ,如果您只想要1个属性,为什么不这样做呢?

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

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

@benwinding不幸的是,返回的形状与mongodb期望的形状不同

@apieceofbart感谢您的建议,我已经研究了它,在接口方面有点多余,但是可以正常工作,我不确定现在是否要实现它,因为如果最终用户尝试有两个运算符的按位条件,无论如何mongo都会引发错误

我试图使mongo-operators的定义尽可能简单,以避免我头疼headache也许将来会添加适当的支持

@ 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 :

不过从技术上来说还是有可能的...

这个stackoverflow答案中,可以使用条件类型(最难的类型)来实现这一点,但是它还不错。

/*
 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,这个_appears_可以达到相同的结果,但是我完全同意它应该是接口的功能,就像在类型上一样。

type Foo = 'a' | 'b';

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

interface A extends Bar { }

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

对于打字稿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
};

虽然我可以为映射类型设置其他未定义的键,但是我更喜欢将这些键设置为可选键,但是如果它们存在,则值不能不确定。 如果我将映射的类型值设为可选,则代码可以工作,但类型不那么强。

使用Partial更好

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

要使用类再添加一个示例...

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

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

很有用。 谢谢! 🚀

此页面是否有帮助?
0 / 5 - 0 等级

相关问题

uber5001 picture uber5001  ·  3评论

bgrieder picture bgrieder  ·  3评论

MartynasZilinskas picture MartynasZilinskas  ·  3评论

Zlatkovsky picture Zlatkovsky  ·  3评论

Antony-Jones picture Antony-Jones  ·  3评论