簡體   English   中英

TypeScript是否可以從動態對象推斷鍵?

[英]Is it possible for TypeScript to infer keys from a dynamic object?

我在這里想要實現的是從數組生成的對象的智能感知/自動完成-類似於Redux的Action Creator,可以將string[]數組( string[] )簡化為形狀為{ [string]: string }

例如:

const a = ['ONE', 'TWO', 'THREE'];

const b = a.reduce((acc, type) => ({ ...acc, [type]: type }), {});

console.log(b);
// Output: b = { ONE: 'ONE', TWO: 'TWO', THREE: 'THREE' };

我已經使用以下方法設法使TypeScript對此有所了解。 TypeScript知道鍵是字符串,但不知道它們是什么。

interface ITypesReturnObject { [s: string]: string }

有沒有人想出一種方法來通知TypeScript對象上的鍵等於數組中的字符串?

任何幫助將不勝感激。

(假設您在下面使用TS3.0或更高版本)

打字稿支持的概念字符串文本類型 ,以及元組類型 ,因此它可以讓你的價值a ['ONE', 'TWO', 'THREE']以及 ['ONE', 'TWO', 'THREE'] ,像這樣:

const a: ['ONE', 'TWO', 'THREE'] = ['ONE', 'TWO', 'THREE'];

(實現這一目標的一種不太冗長的方法將在以后出現):

然后,您可以使用映射類型將b的預期類型表示為從鍵到與鍵完全匹配的值的映射

type SameValueAsKeys<KS extends string[]> = { [K in KS[number]]: K };

可以這樣使用:

const b: SameValueAsKeys<typeof a> = a.reduce((acc, type) => ({ ...acc, [type]: type }), {} as any);
b.ONE; // property type is "ONE"
b.TWO; // property type is "TWO"
b.THREE; // property type is "THREE"

請注意,編譯器如何知道b具有三個鍵"ONE""TWO""THREE" ,並且值與這些鍵相同。


因此,TypeScript當然支持這種類型的動態類型。 不幸的是,如上所示,使用起來有點繁瑣。 減少煩人的一種方法是添加一些幫助程序函數,這些函數允許編譯器推斷正確的類型,或者至少將類型斷言隱藏在開發人員無需擔心它們的庫中。

首先, a ...編譯器不會推斷的元組類型,它也傾向於擴大字符串文字的string類型,除了在某些情況下 讓我們介紹一個名為stringTuple()的輔助函數:

function stringTuple<T extends string[]>(...args: T) { return args; }

從其余參數推斷出元組類型 現在,我們可以使用它來創建a而無需鍵入多余的字符串值:

const a = stringTuple("ONE", "TWO", "THREE");

接下來,讓我們介紹一個函數,該函數獲取字符串列表並返回一個對象,該對象的鍵是那些字符串,並且其值與這些字符串匹配:

function keyArrayToSameValueAsKeys<T extends string[]>(keys: T): SameValueAsKeys<T>;
function keyArrayToSameValueAsKeys(keys: string[]): { [k: string]: string } {
  return keys.reduce((acc, type) => ({ ...acc, [type]: type }), {});
}

在這里,我們使用與reduce相同的代碼,但將其隱藏在自己的函數中,並使用單個重載調用簽名來表示預期的輸出類型。 現在,我們可以這樣得到b

const b = keyArrayToSameValueAsKeys(a);
b.ONE; // property type is "ONE"
b.TWO; // property type is "TWO"
b.THREE; // property type is "THREE"

如果將stringTuple()keyArrayToSameValueAsKeys()放在庫中,則用戶可以輕松使用它們:

const otherObject = keyArrayToSameValueAsKeys(stringTuple("x", "y", "z"));
// const otherObject: {X: "X", Y: "Y", Z: "Z"}

或者,您可以將它們合並在一起,如下所示:

function keysToSameValueAsKeys<T extends string[]>(...keys: T): { [K in T[number]]: K };
function keysToSameValueAsKeys(keys: string[]): { [k: string]: string } {
  return keys.reduce((acc, type) => ({ ...acc, [type]: type }), {});
}

然后在一個調用中獲得輸出,如下所示:

const lastOne = keysToSameValueAsKeys("tic", "tac", "toe");
// const lastOne: {tic: "tic", tac: "tac", toe: "toe"};

好的,希望對您有所幫助。 祝好運!

如果您事先知道數組中將包含的內容,則可以執行以下操作:

type myType = 'ONE' | 'TWO' | 'THREE';
const a: myType[] = ['ONE', 'TWO', 'THREE'];

const b: { [key in myType]: myType } = a.reduce<{ [key in myType]: myType }>((acc, type) => ({ ...acc, [type]: type }), <any>{});

// correct intellisense
b.ONE === 'ONE'

來到這里尋找更好的答案。 這是我想出的,效果很好:

const makeStyles = <T extends string = string>(styles: Record<T, string>): Record<T, string> => {
  return styles;
}

const classes = makeStyles({
  hello: 'hello world',
  another: 'something else'
})

/** 
 * now typescript will infer the keynames that appear in the object passed 
 * to makeStyles
 */
console.log(classes)


暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM