使用 Object.keys(obj)
时,返回值为 string[]
,而我想要 (keyof obj)[]
。
const v = {
a: 1,
b: 2
}
Object.keys(v).reduce((accumulator, current) => {
accumulator.push(v[current]);
return accumulator;
}, []);
我有错误:
元素隐含地具有“任何”类型,因为类型 '{ a: number; b:号码; }' 没有索引签名。
带有 strict: true
的 TypeScript 3.1。 Playground:here,请选中 Options
中的所有复选框以激活 strict: true
。
(Object.keys(v) as Array<keyof typeof v>)
定义就是这样
object-typed
包比在任何地方都使用这种类型断言更清晰:ObjectTyped.keys(v)
。只需 npm i object-typed
然后导入它。
Object.keys
返回一个 string[]
。这是本 issue 中所述的设计
这是故意的。 TS 中的类型是开放式的。因此,keysof 可能会少于您在运行时获得的所有属性。
有几种解决方案,最简单的一种是只使用类型断言:
const v = {
a: 1,
b: 2
};
var values = (Object.keys(v) as Array<keyof typeof v>).reduce((accumulator, current) => {
accumulator.push(v[current]);
return accumulator;
}, [] as (typeof v[keyof typeof v])[]);
您还可以在 Object
中为 keys
创建一个别名,该别名将返回您想要的类型:
export const v = {
a: 1,
b: 2
};
declare global {
interface ObjectConstructor {
typedKeys<T>(obj: T): Array<keyof T>
}
}
Object.typedKeys = Object.keys as any
var values = Object.typedKeys(v).reduce((accumulator, current) => {
accumulator.push(v[current]);
return accumulator;
}, [] as (typeof v[keyof typeof v])[]);
基于 Titian Cernicova-Dragomir 的回答和评论
仅当您知道您的对象没有额外的属性时才使用类型断言(例如对象文字而不是对象参数的情况)。
显式断言
Object.keys(obj) as Array<keyof typeof obj>
隐藏断言
const getKeys = Object.keys as <T extends object>(obj: T) => Array<keyof T>
使用 getKeys
而不是 Object.keys
。 getKeys
是对 Object.keys
的引用,但返回是按字面输入的。
讨论
TypeScript 的核心原则之一是类型检查侧重于值的形状。 (参考)
interface SimpleObject {
a: string
b: string
}
const x = {
a: "article",
b: "bridge",
c: "Camel"
}
x
有资格作为 SimpleObject
,因为它有它的形状。这意味着当我们看到 SimpleObject
时,我们知道它具有属性 a
和 b
,但它也可能具有其他属性。
const someFunction = (obj: SimpleObject) => {
Object.keys(obj).forEach((k)=>{
....
})
}
someFunction(x)
让我们看看如果默认情况下我们按照 OP“字面意思”的需要键入 Object.keys 会发生什么:
我们会得到 typeof k
是 "a"|"b"
。迭代时,实际值为 a
、b
、c
。 Typescript 通过将 k 键入为字符串来保护我们免受此类错误的影响。
类型断言正是针对这种情况的——当程序员有额外的知识时。如果您知道 obj
没有额外的属性,则可以使用文字类型断言。
请参阅https://github.com/microsoft/TypeScript/issues/20503。
declare const BetterObject: {
keys<T extends {}>(object: T): (keyof T)[]
}
const icons: IconName[] = BetterObject.keys(IconMap)
将保留键类型而不是 string[]
window.BetterObject = {keys: Object.keys}
的实际 javascript 定义
我完全不同意 Typescript 团队的决定...
按照他们的逻辑,Object.values
应该始终返回任何内容,因为我们可以在运行时添加更多属性...
我认为正确的方法是创建具有可选属性的接口并在您进行时设置(或不设置)这些属性......
所以我只是简单地在本地覆盖了 ObjectConstructor
接口,方法是在我的项目中添加一个声明文件(又名:whatever.d.ts),其中包含以下内容:
declare interface ObjectConstructor extends Omit<ObjectConstructor, 'keys' | 'entries'> {
/**
* Returns the names of the enumerable string properties and methods of an object.
* @param obj Object that contains the properties and methods. This can be an object that you created or an existing Document Object Model (DOM) object.
*/
keys<O extends any[]>(obj: O): Array<keyof O>;
keys<O extends Record<Readonly<string>, any>>(obj: O): Array<keyof O>;
keys(obj: object): string[];
/**
* Returns an array of key/values of the enumerable properties of an object
* @param obj Object that contains the properties and methods. This can be an object that you created or an existing Document Object Model (DOM) object.
*/
entries<T extends { [K: Readonly<string>]: any }>(obj: T): Array<[keyof T, T[keyof T]]>
entries<T extends object>(obj: { [s: string]: T } | ArrayLike<T>): [string, T[keyof T]][];
entries<T>(obj: { [s: string]: T } | ArrayLike<T>): [string, T][];
entries(obj: {}): [string, any][];
}
declare var Object: ObjectConstructor;
笔记:
原始类型(对象)的 Object.keys/Object.entries 将返回 never[] 和 [never, never][] 而不是正常的 string[] 和 [string, any][]。如果有人知道解决方案,请随时在评论中告诉我,我将编辑我的答案
const a: {} = {};
const b: object = {};
const c: {x:string, y:number} = { x: '', y: 2 };
// before
Object.keys(a) // string[]
Object.keys(b) // string[]
Object.keys(c) // string[]
Object.entries(a) // [string, unknown][]
Object.entries(b) // [string, any][]
Object.entries(c) // [string, string|number][]
// after
Object.keys(a) // never[]
Object.keys(b) // never[]
Object.keys(c) // ('x'|'y')[]
Object.entries(a) // [never, never][]
Object.entries(b) // [never, never][]
Object.entries(c) // ['x'|'y', string|number][]
所以,请谨慎使用...
never
的情况,但我不确定。
interface ObjectConstructor extends Omit<ObjectConstructor,
似乎不正确/错误,因为 ObjectContructor
递归引用自身
伟大的斯科特!
现在有一个不错的 npm 包:
npm install object-typed --save
用法:
import { ObjectTyped } from 'object-typed'
ObjectTyped.keys({ a: 'b' })
这将返回一个 ['a']
类型的数组
(不是开发依赖项,因为您将其import
放入您的应用程序 src
)
您可以使用 Extract 实用程序类型使您的参数仅符合 obj
的 keys
字符串(因此,在您编码时忽略任何数字/符号)。
const obj = {
a: 'hello',
b: 'world',
1: 123 // 100% valid
} // if this was the literal code, you should add ` as const` assertion here
// util
type StringKeys<objType extends {}> = Array<Extract<keyof objType, string>>
// typedObjKeys will be ['a', 'b', '1'] at runtime
// ...but it's type will be Array<'a' | 'b'>
const typedObjKeys = Object.keys(obj) as StringKeys<typeof obj>
typedObjKeys.forEach((key) => {
// key's type: 'a' | 'b'
// runtime: 'a', 'b', AND '1'
const value = obj[key]
// value will be typed as just `string` when it's really `string | number`
})
尽管如此,大多数开发人员可能会认为将数字作为键是一个糟糕的设计决策/要修复的错误。
type Extract<T, U> = T extends U ? T : never
和描述:Extract from T those types that are assignable to U
Extract
的实用性。这是一个不同的示例:typescriptlang.org/play?#code/…
这是我用于以类型安全方式复制对象的模式。它使用字符串缩小,因此编译器可以推断键实际上是类型。这通过一个类进行了演示,但可以在接口或相同形状的匿名类型之间工作。
它有点冗长,但可以说比公认的答案更直接。如果您必须在多个地方进行复制操作,它确实可以节省打字。
请注意,如果类型不匹配,这将抛出错误,但如果 thingNum
中缺少字段,不会抛出错误.所以这可能是 Object.keys 的一个缺点。
class thing {
a: number = 1;
b: number = 2;
}
type thingNum = 'a' | 'b';
const thingNums: thingNum[] = ['a', 'b'];
const thing1: thing = new thing();
const thing2: thing = new thing();
...
thingNums.forEach((param) => {
thing2[param] = thing1[param];
});
作为一种可能的解决方案,您可以在对象上使用 for..in
进行迭代:
for (const key in myObject) {
console.log(myObject[key].abc); // works, but `key` is still just `string`
}
正如您所说,虽然这不起作用:
for (const key of Object.keys(myObject)) {
console.log(myObject[key].abc); // doesn't!
}
obj[k]
上有红色下划线:Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'Obj'. No index signature with a parameter of type 'string' was found on type 'Obj'.(7053)
key: string
keyof
运算符不会返回与Objects.keys
相同的内容Object
withtypedKeys
(or anything you don't own), please take some time to read the pros and cons.Object
的本机属性。例如:function getTypedKeys<T>(obj: T): Array<keyof T> { return Object.keys(obj) as Array<keyof typeof obj>; }
。然后,在您通常写Object.keys(obj)
的地方,您改为写getTypedKeys(obj)
。