使用Object.keys(obj)
时,返回值为string[]
,而我想要的是(keyof obj)[]
。
const v = {
a: 1,
b: 2
}
Object.keys(v).reduce((accumulator, current) => {
accumulator.push(v[current]);
return accumulator;
}, []);
我有错误:
元素隐式具有“ any”类型,因为类型为“ {{a:number; b:数字; }'没有索引签名。
带有strict: true
的TypeScript 3.1。游乐场:here,请选中Options
中的所有复选框以激活strict: true
。
答案 0 :(得分:5)
请参见https://github.com/microsoft/TypeScript/issues/20503。
.TextFileColumnDataTypes = arrV
将保留键的类型,而不是declare const BetterObject: {
keys<T extends {}>(object: T): (keyof T)[]
}
const icons: IconName[] = BetterObject.keys(IconMap)
答案 1 :(得分:4)
Object.keys
返回一个string[]
。这是根据issue
这是故意的。 TS中的类型是开放式的。因此keyof可能会小于您在运行时获得的所有属性。
有几种解决方案,最简单的一种就是只使用类型断言:
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])[]);
您还可以在keys
中为Object
创建别名,该别名将返回所需的类型:
export const v = {
a: 1,
b: 2
};
declare global {
interface ObjectConstructor {
typedKeys<T>(o: 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])[]);
答案 2 :(得分:2)
我完全不同意Typescript团队的决定...
按照其逻辑,Object.values
应该始终返回任何值,因为我们可以在运行时添加更多属性...
我认为正确的方法是创建具有可选属性的接口,并在您进行操作时设置(或不设置)这些属性...
因此,我只需在项目中添加以下内容的声明文件(aka:whatever.d.ts)即可在本地覆盖ObjectConstructor
接口。
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][]
因此,请谨慎使用 ...
答案 3 :(得分:0)
作为一种可能的解决方案,您可以在对象上使用for..in
进行迭代:
for (const key in myObject) {
console.log(myObject[key].abc); // works!
}
正如您所说,这是行不通的:
for (const key of Object.keys(myObject)) {
console.log(myObject[key].abc); // doesn't!
}
答案 4 :(得分:0)
您可以使用Extract实用程序类型使您的参数仅符合obj的keyof类型(即字符串)(无论如何)。
Object.keys(obj).forEach((key: Extract<keyof typeof obj, string>) => {
const item = obj[key]
})
答案 5 :(得分:0)
您可以使用以下常规函数包装器
const getObjectKeys = <O extends object>(obj:O) => Object.keys(obj) as Array<keyof O>
答案 6 :(得分:0)
Object.typedKeys = Object.keys
这是一个精致的声明,您可以在src/index.js
declare global {
interface ObjectConstructor {
/**
* Object.keys, but with nice typing (e.g.`Array<keyof T>`)
*
* PSA: Don't mutate `yourObject`s
*
* Don't use numerical keys, your types will be awkward.
* - `{ 1 : 123 }` is bad
* - `{ "1": 123 }` is... better...
*
* Object.keys coerces numerical keys to strings anyway.
* See https://stackoverflow.com/a/65117465/565877
*/
typedKeys<T extends {}>(yourObject: T): Array<keyof T>
}
}
Object.typedKeys = Object.keys as any
将鼠标悬停在Object.typedKeys上会显示以下内容:
避免将此导入到任何地方都很好。这种方法避免了在整个代码库中以任何方式改变Object.keys
行为 。这是一个明确的选择加入。定义Object.typedKeys
的JavaScript就在TS声明的旁边。
当然,开发人员需要发现Object.typedKeys
,但是,如果在整个代码库中都使用了它,发现它就不会花很长时间。
======
这是一个很好的非全局版本,您必须将其导入到任何地方,如果您具有vscode自动导入扩展名,那就可以了:
export const Object_typedKeys = Object.keys as <
T extends object //
>(
obj: T
) => Array<keyof T>
这两个函数都避免使用Extract<keyof T, string>
,因为它们打算在整个代码库中使用,所以它可能不是最安全的默认设置(您可能想知道是否有非字符串键并处理视情况而定,或解决非字符串键的根本原因)
如果只想始终查看字符串键,可以将Array<keyof T>
更改为Array<Extract<keyof T, string>>
,并且在大多数情况下还是可以的。
=====
有关Extract
和类型混合的更多信息:
const obj = {
a: "hello",
b: "world",
1: 100
} as const
type Keys<objType extends {}> = Array<keyof objType>
type StrKeys<objType extends {}> = Array<Extract<keyof objType, string>>
const typedObjKeys = Object.keys(obj) as Keys<typeof obj>
typedObjKeys.forEach(
(key) => { // key is 'a' | 'b' | 1 but should be 'a' | 'b' | '1'
// If you need to pass `key` to a function that expects a string,
// use Extract at the last possible moment:
giveMeString(key as Extract<typeof key, string>)
// giveMeString will see 'a' | 'b'
// String(key) is not as nice as Extract, since
// giveMeString will only see `string`, not 'a' | 'b' #TS_FIX_ME
// Note: there is no way to convert a literal number to it's literal string
const aNumber = 1 as const
// no matter how you convert a number to a string,
// you can't get the type to be '1' instead of `string` (v4.1.2)
const itsString = aNumber.toString()
// `value` is correctly typed:
const value = obj[key]
// with `as const`: 'hello' | 'world' | 100
// w/o `as const`: string | number
}
)