TypeScript: Object.keys מחזיר string[]
כאשר משתמשים ב-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: number; }' אין חתימת אינדקס.
TypeScript 3.1 עם strict: true. מגרש משחקים: כאן, אנא סמן את כל התיבות ב-Options כדי להפעיל את strict: true.
תשובה #1
Object.keys מחזיר string[]. זה נכון לפי העיצוב כפי שמתואר בבעיה זו
זה מכוון. סוגים ב-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])[]);
אתה גם יכול ליצור כינוי עבור keys ב-Object שיחזיר את הסוג שאתה רוצה:
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])[]);
תשובה #2
בהתבסס על התשובה והתגובה של טיצ'יאן צ'רניקובה-דראגומיר
השתמש בהצהרת סוג רק אם אתה יודע שהאובייקט שלך אינו מכיל תכונות נוספות (כפי שקורה במקרה של אובייקט ליטרלי אך לא בפרמטר של אובייקט).
טענה מפורשת
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)
בואו נראה מה יקרה אם כברירת מחדל נקליד Object.keys כפי שהאופן רוצה "ליטרלית":
נקבל כי typeof k הוא "a"|"b". בעת חזרת על הערכים בפועל הם יהיו a, b, c. TypeScript מגן עלינו מפני שגיאה כזו על ידי הקצאת טיפוס מחרוזת ל-k.
אישור סוג הוא בדיוק למקרים כאלה - כאשר למתכנת יש ידע נוסף. אם אתה יודע של-obj אין תכונות נוספות, אתה יכול להשתמש באישור סוג מילולי.
תשובה #3
אני לחלוטין לא מסכים עם החלטת הצוות של Typescript...
בעקבות ההיגיון שלהם, Object.values תמיד צריך להחזיר any, מכיוון שנוכל להוסיף עוד תכונות בזמן ריצה...
אני חושב שהדרך הנכונה היא ליצור ממשקים עם תכונות אופציונליות ולהגדיר (או לא) את אותן תכונות תוך כדי התקדם...
אז פשוט דרסתי מקומית את הממשק 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 מסוגים פרימיטיביים (object) יחזירו לעולם לא[] ו-[לעולם, לעולם לא][] במקום המחרוזת הרגילה[] ו-[מחרוזת, כל][]. אם מישהו יודע פתרונות, בבקשה, אל תהסס לספר לי בתגובות ואני אערוך את תשובתי
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][]
אז, השתמש בזה בזהירות...
תשובה #4
1. npm install ts-extras (נכתב על ידי sindresorhus)
- השתמש בזה:
import { objectKeys } from 'ts-extras'
objectKeys(yourObject)
זהו.
הנה חבילה נוספת שיצרתי לפני שידעתי על ts-extras:
npm install object-typed --save
import { ObjectTyped } from 'object-typed'
ObjectTyped.keys({ a: 'b' })
זה יחזיר מערך מסוג ['a']
