Typescript Genel Dizi'yi uygun tür kısıtlamalarıyla Kayıt fonksiyonuna dönüştürme
Bir array: T[] ve bir seçici K alan ve dizideki her öğeyi bir özelliğinden kendisine eşleyen bir Record<T[K], T> döndüren genel bir işlev yazmaya çalışıyorum.
ör.
const input1 = [{ name: "Alice" }, { name: "Bob" }];
const output1: Record<string, { name: string }> = toRecord(input1, "name");
// { Alice: { name: "Alice" }, Bob: { name: "Bob" } }
// -----------------------------------------------------------------
enum Name {
Alice = "Alice",
Bob = "Bob"
}
const input2 = [{ name: Name.Alice }, { name: Name.Bob }];
const output2: Record<Name, { name: Name }> = toRecord(input2, "name");
// { Alice: { name: "Alice" }, Bob: { name: "Bob" } }
// -----------------------------------------------------------------
interface Person {
id: number;
name: string;
sibling?: Person;
}
const input3: Person[] = [{ id: 1, name: "Alice" }, { id: 2, name: "Bob" }];
const output3: Record<number, Person> = toRecord(input3, "id");
// { 1: { id: 1, name: "Alice" }, 2: { id: 2, name: "Bob" } }
Seçici özelliği K türünü, T[K] için Record'un anahtar olarak kabul edeceği türde yani string | number | symbol olacak şekilde nasıl kısıtlayacağımı çözemedim.
Şu ana kadar elimde olan bu:
export function toRecord<T, K extends keyof T>(
array: T[],
selector: K
): Record<T[K], T> {
return array.reduce(
(acc, item) => (acc[item[selector]] = item, acc),
{} as Record<T[K], T>
)
}
Ama bu bana T[K] üzerinde aşağıdaki hatayı veriyor:
Type 'T[K]' does not satisfy the constraint 'string | number | symbol'. Type 'T[keyof T]' is not assignable to type 'string | number | symbol'. Type 'T[string] | T[number] | T[symbol]' is not assignable to type 'string | number | symbol'. Type 'T[string]' is not assignable to type 'string | number | symbol'. Type 'T[string]' is not assignable to type 'symbol'. Type 'T[keyof T]' is not assignable to type 'symbol'. Type 'T[K]' is not assignable to type 'symbol'. Type 'T[keyof T]' is not assignable to type 'symbol'. Type 'T[string] | T[number] | T[symbol]' is not assignable to type 'symbol'. Type 'T[string]' is not assignable to type 'symbol'.(2344)
T[K] string | number | symbol olacak şekilde K'yi nasıl kısıtlayabilirim?
Stackblitz
Cevap #1
Typescript, T değerlerinin anahtar olarak geçerli olduğunu bilmiyor.
‘T[K]’ türü ‘string | number | symbol’ kısıtını karşılamıyor.
Bu, T'nin { a: boolean } veya { a:() => void } olabileceğini söylüyor ve bu, T[K]'nin bir boolean veya bir fonksiyon olabileceği anlamına gelir, bunların hiçbiri nesne özellik adları olarak geçerli değildir.
Bu yüzden T öğesini sadece anahtar olarak kullanılmasına izin verilen değerlere sahip bir nesne ile sınırlardım:
function toRecord<
T extends { [K in keyof T]: string | number | symbol }, // added constraint
K extends keyof T
>(array: T[], selector: K): Record<T[K], T> {
return array.reduce((acc, item) => (acc[item[selector]] = item, acc), {} as Record<T[K], T>)
}
Oyun alanı
Görünüşe göre anahtar olarak uygun olmayabilecek özellikleriniz var. Bunu yine de güçlü bir şekilde yazabilirsiniz, sadece özelliğin değeri bir nesne ise, bir anahtar olamayacağını bilmelisiniz; bu yüzden bunlar filtrelenmelidir. Daha önce belirtildiği gibi, bir nesne özelliği adının türü string | number | symbol'dür. O yüzden bu değerleri tutalım ve geri kalanını atlayalım.
type RecordableKeys<T> = {
// for each key in T
[K in keyof T]:
// is the value a valid object key?
T[K] extends string | number | symbol
// Yes, return the key itself
? K
// No. Return `never`
: never
}[keyof T] // Get a union of the values that are not `never`.
interface Person {
name: string,
age: number
sibling?: Person
}
type PersonRecordableKeys = RecordableKeys<Person> // 'name' | 'age'
Bu yardımcı tür yerinde olduğunda, toRecord içindeki T kısıtlamasını şuna değiştirebiliriz:
function toRecord<
T extends { [P in RecordableKeys<T>]: string | number | symbol },
K extends RecordableKeys<T>
>
Oyun alanı
Cevap #2
Typescript, T'nin aslında bir kayıt olduğunu kesin olarak bilmek istiyor:
export function toRecord<T extends Record<string, any>, K extends keyof T>(array: T[], selector: K): Record<T[K], T> {
return array.reduce((acc, item) => ({ ...acc, [item[selector]]: item }), {} as Record<T[K], T>)
}
