TypeScript:Object.keys返回字符串[]
使用TypeScript:Object.keys返回字符串[],typescript,Typescript,使用Object.keys(obj)时,返回值是字符串[],而我需要(keyof obj)[ const v = { a: 1, b: 2 } Object.keys(v).reduce((accumulator, current) => { accumulator.push(v[current]); return accumulator; }, []); 我有一个错误: 元素隐式具有“any”类型,因为类型“{a:number;b:number;}”没
Object.keys(obj)
时,返回值是字符串[]
,而我需要(keyof obj)[
const v = {
a: 1,
b: 2
}
Object.keys(v).reduce((accumulator, current) => {
accumulator.push(v[current]);
return accumulator;
}, []);
我有一个错误:
元素隐式具有“any”类型,因为类型“{a:number;b:number;}”没有索引签名
键入脚本3.1,带有
严格:true
。操场:,请选中选项
中的所有复选框以激活严格:true
对象。键
返回一个字符串[]
。这是本节中所述的设计
这是故意的。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])[]);
作为一种可能的解决方案,您可以在对象上使用中的for..进行迭代:
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
的键一致,这些键是字符串(因此,在编码时忽略任何数字/符号)
const obj={
a:你好,
b:‘世界’,
1:123//100%有效
}//如果这是文字代码,则应在此处添加'as const'断言
//利用率
键入StringKeys=Array
//typedObjKeys在运行时将为['a','b','1']
//…但它的类型将是数组
const typedObjKeys=Object.keys(obj)作为StringKeys
typedObjKeys.forEach((键)=>{
//钥匙类型:“a”|“b”
//运行时:“a”、“b”和“1”
常量值=对象[key]
//值的类型将仅为'string',而实际上为'string | number`
})
所有的开发者都可能认为数字是关键的一个糟糕的设计决策/ bug,基于Titian Cernicova Dragomir的回答和评论
仅当您知道对象没有额外属性时才使用类型断言(这是对象文字而不是对象参数的情况)
明确断言
Object.keys(obj) as Array<keyof typeof obj>
const getKeys = Object.keys as <T extends object>(obj: T) => Array<keyof T>
x
符合SimpleObject
的条件,因为它有自己的形状。这意味着当我们看到一个SimpleObject
时,我们知道它有属性a
和b
,但它也可能有其他属性
const someFunction = (obj: SimpleObject) => {
Object.keys(obj).forEach((k)=>{
....
})
}
someFunction(x)
让我们看看如果默认情况下,我们按照OP“字面上”的要求键入Object.keys会发生什么:
我们将得到k的类型是“a”|“b”
。迭代时,实际值为a
,b
,c
。Typescript通过将k作为字符串键入来保护我们不受此类错误的影响
类型断言正是针对这种情况——当程序员有额外的知识时。如果您知道obj
没有额外的属性,可以使用文字类型断言。请参阅
declare const BetterObject:{
键(对象:T):(键of T)[]
}
常量图标:IconName[]=BetterObject.keys(IconMap)
将保留密钥类型而不是字符串[]
我完全不同意Typescript团队的决定…
按照它们的逻辑,Object.values
应该总是返回any,因为我们可以在运行时添加更多属性
我认为正确的方法是创建具有可选属性的接口,并在运行时设置(或不设置)这些属性
因此,我只是在本地重写了ObjectConstructor
接口,向我的项目添加了一个声明文件(aka: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;
因此,谨慎使用此方法…导入方法
如果您可能在monorepo中工作,或者考虑10年的前景,那么您可能更喜欢基于导入的方法(无论如何,在vscode中导入是非常容易的,只需点击
)
只要把它放在某个地方,然后键入ObjectT
hit
,当使用vscode时,它就会自动导入
type KeyOf<T> = Extract<keyof T, string>
type ValueOf<T> = T[KeyOf<T>]
/**
* Nicely typed aliases for some `Object` Methods
* - PSA: Don't mutate `yourObject`s
* - Numerical keys are BAD `{ 1: 'ha!' }` may not appear in your resulting types
* - Discussion: https://stackoverflow.com/a/65117465/565877
*/
export const ObjectTyped = {
/**
* Object.keys, but with nice typing (`Array<keyof T>`)
*/
keys: Object.keys as <T extends {}>(yourObject: T) => Array<KeyOf<T>>,
/**
* Object.values, but with nice typing
*/
values: Object.values as <T extends {}>(yourObject: T) => Array<ValueOf<T>>,
/**
* Object.entries, but with nice typing
*/
entries: Object.entries as <T extends {}>(yourObject: T) => Array<[KeyOf<T>, ValueOf<T>]>,
/**
* Object.fromEntries, but with nice typing
*/
fromEntries: Object.fromEntries as <K extends string, V>(
yourObjectEntries: Array<[K, V]>
) => Record<K, V>,
}
由于Extract
typescript将对您隐藏此数字键。但是,如果您了解javascript,您可能知道Object.keys
将1
转换为字符串“1”
。理想情况下,typescript只是对Object.keys有更好的默认类型,但有时你不能有蛋糕吃
如果您只想始终看到字符串键,则可以将Array
更改为Array
,并且在大多数情况下您仍然可以正常工作。
是的,我们现在正在做一件非常明显的事情。不要认为你能比类型断言(Object.keys(v)as Array)做得更好。
定义就是这样的,你能解释一下为什么“所以keysof可能比运行时得到的所有属性都要少。”?@user2010955这是GitHub线程的一段引用,我的理解是,由于您可以向JS的任何对象添加更多属性,因此keyof
操作符不会返回与对象相同的内容。keys
还有一个属性显示了它们的思维过程。正如@EmileBergeron指出的,您可以考虑定义自己的本地函数,而不是扩展<代码>对象< /代码>的本机属性。例如:函数getTypedKeys(obj:T):数组{return Object.keys(obj)as Array;}
。然后,在通常编写Object.keys(obj)
的地方,改为编写getTypedKeys(obj)
。不适用于。在中,在给定的游乐场链接中,k只是一个字符串,一定有一些TS错误导致此操作现在失败。它当前失败,在obj[k]
上有一条红色下划线:元素隐式地具有“any”类型,因为类型为“string”的表达式不能用于索引类型“obj”。在类型“Obj”上找不到参数类型为“string”的索引签名。(7053)
使用TS 4.1.2,因为..in-loop似乎与当前对象没有什么不同。keys,使用这两个键,您只需选择键:string
即可回答!这是非常清楚和简洁的。。
const someFunction = (obj: SimpleObject) => {
Object.keys(obj).forEach((k)=>{
....
})
}
someFunction(x)
declare const BetterObject: {
keys<T extends {}>(object: T): (keyof T)[]
}
const icons: IconName[] = BetterObject.keys(IconMap)
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;
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][]
type KeyOf<T> = Extract<keyof T, string>
type ValueOf<T> = T[KeyOf<T>]
/**
* Nicely typed aliases for some `Object` Methods
* - PSA: Don't mutate `yourObject`s
* - Numerical keys are BAD `{ 1: 'ha!' }` may not appear in your resulting types
* - Discussion: https://stackoverflow.com/a/65117465/565877
*/
export const ObjectTyped = {
/**
* Object.keys, but with nice typing (`Array<keyof T>`)
*/
keys: Object.keys as <T extends {}>(yourObject: T) => Array<KeyOf<T>>,
/**
* Object.values, but with nice typing
*/
values: Object.values as <T extends {}>(yourObject: T) => Array<ValueOf<T>>,
/**
* Object.entries, but with nice typing
*/
entries: Object.entries as <T extends {}>(yourObject: T) => Array<[KeyOf<T>, ValueOf<T>]>,
/**
* Object.fromEntries, but with nice typing
*/
fromEntries: Object.fromEntries as <K extends string, V>(
yourObjectEntries: Array<[K, V]>
) => Record<K, V>,
}
{
1: 'asdf'
}