string 'T' tipini indekslemek için kullanılamaz
TypeScript'te katı mod etkin ve türüne bir dize indeksleyici kurmuş olsam bile bir nesnede bir indeks ayarlamayla ilgili sorunlar yaşıyorum.
NoImplicitAny'nin devre dışı bırakılması sorunu düzeltir, ancak bunu tercih etmem.
Bu cevabı yakından inceledim ama önerilerle başarılı olamadım.
type stringKeyed = { [key: string]: any }
type dog = stringKeyed & { name: string }
type cat = stringKeyed & { lives: number }
function setLayerProps<T extends dog | cat>(
item: T,
props: Partial<T>
) {
if (item) {
Object.entries(props).forEach(([k, v]) => {
item[k] = v; // Error: Type 'string' cannot be used to index type 'T'.ts(2536)
});
}
}
let d = { name: 'fido' } as dog;
let c = { lives: 9 } as cat;
setLayerProps(d, { name: 'jim' })
setLayerProps(c, { lives: --c.lives })
Neyi yanlış yaptığımı gören var mı?
Teşekkürler!
Yanıtlar
Bu, # 30769'un bir etkisidir ve bilinen bir kırılma değişikliğidir. Daha önce böyle çılgın şeylere hatasız izin vermiştik:
function foo<T extends Record<string, any>>(obj: T) {
obj["s"] = 123;
obj["n"] = "hello";
}
let z = { n: 1, s: "abc" };
foo(z);
foo([1, 2, 3]);
foo(new Error("wat"));
Genel olarak, Record <string, XXX> kısıtlaması, bir bağımsız değişkenin bir dize indeksi imzasına sahip olmasını sağlamaz, yalnızca bağımsız değişkenin özelliklerinin XXX türüne atanabilir olmasını sağlar. Dolayısıyla, yukarıdaki örnekte herhangi bir nesneyi etkili bir şekilde iletebilirsiniz ve işlev herhangi bir denetim olmadan herhangi bir özelliğe yazabilir.
3.5'te, yalnızca söz konusu nesnenin bir dizin imzasına sahip olduğunu bildiğimizde bir dizin imza öğesine yazabileceğinizi zorlarız. Yani, klon işlevinde küçük bir değişikliğe ihtiyacınız var:
function clone<T extends Record<string, any>>(obj: T): T {
const objectClone = {} as Record<string, any>;
for (const prop of Reflect.ownKeys(obj).filter(isString)) {
objectClone[prop] = obj[prop];
}
return objectClone as T;
}
Bu değişiklikle artık objectClone'un bir dize indeksi imzası olduğunu biliyoruz.
Yani kodunuz
function setLayerProps<T extends dog | cat>(
item: T,
props: Partial<T>
) {
if (item) {
Object.entries(props).forEach(([k, v]) => {
(item as dog | cat)[k] = v;
});
}
}
Referans: https://github.com/microsoft/TypeScript/issues/31661#issuecomment-497138929
Sorumluluk reddi: Bu gerçekten bir cevap değil, bu konu etrafında dolaşmamın sonucudur. Açıklık adına bir cevap yazıyorum.
TL; DR: Nesne anahtarları türü string | number, değilstring
Çalışmasını sağlamak
Ben kullanılan typescript oyun alanı ve ile sona erdi çalışıyor şey typescript 4.0.2 altında:
type stringKeyed = { [key: string]: any };
type dog = stringKeyed & { name: string };
type cat = stringKeyed & { lives: number };
function setLayerProps<T extends dog | cat>(item: T, props: Partial<T>) {
if (item) {
Object.entries(props).forEach(([k, v]) => {
// With an explicit key casting, TypeScript does not complain
const key = k as keyof T;
item[key] = v;
});
}
}
let d = { name: "fido" } as dog;
let c = { lives: 9 } as cat;
setLayerProps(d, { name: "jim" });
setLayerProps(c, { lives: --c.lives });
console.log(d); // [LOG]: { "name": "jim" }
console.log(c); // [LOG]: { "lives": 8 }
Neden işe yarıyor?
Kavşak türü ... bir kavşak değil
TypeScript belgelerine göre :
Bir kesişim türü, birden çok türü bir arada birleştirir. Bu, ihtiyacınız olan tüm özelliklere sahip tek bir tür elde etmek için mevcut türleri bir araya getirmenize olanak tanır.
Kahretsin, keşke daha önce bu iki satıra daha çok dikkat etsem.
İfade kesişimi beni kandırdı ve bu dogtipin yalnızca bir tuşa ( name) izin vereceğini düşündüm, ancak durum böyle değil. Coddingblast'taki TypeScript makalesinde yer alan Kesişim türlerine göre , kesişim türü, alt türü kesişen türlerden tüm özellikleri miras alır.
Başka bir deyişle:
type stringKeyed = { [key: string]: any };
type dog = stringKeyed & { name: string };
anlamına gelir
- İçin
nameanahtarın, bir dize değeri bekleniyor - Diğer herhangi bir anahtar için herhangi bir değere izin verilir
Bu durumda aşağıdaki beyan doğrudur:
let d: dog = {name: 'fido' };
d.something = else;
d.age = 3;
Bu aşamada, çıkarmak istediğimiz tek sonuç, tüm doganahtarların yalnızca dizedir . Şey ... tam olarak değil.
JavaScript ... bir sayı anahtarı ile bir dize anahtarı arasındaki farkı bilmiyor T_T
Bu mükemmel StackOverflow cevabından alıntı yapmak için :
TypeScript 2.9 Sürüm Notlarında tanımlandığı gibi, bir dize indeksi imzasına sahip bir arabirim anahtarlarsanız, bir dizi ve sayı birleşimi döndürür.
Ve typescript belgeleri bahseder:
Türün bir dize veya sayı indeksi imzası varsa, keyof bunun yerine şu türleri döndürür:
type Mapish = { [k: string]: boolean }; type M = keyof Mapish; // ^ = type M = string | number
Kendimi ikna etmek için şunları denedim:
type keys = keyof stringKeyed;
type dogKeys = keyof dog;
const keyAsStr: dogKeys = "this is OK";
const keyAsNum: dogKeys = 42;
Hem keysve dogKeyskarar verin string | number.
string! == string | numara
Şimdi, Error: Type 'string' türünün 'T' .ts (2536) tipini indekslemek için kullanılamamasının nedeni, sadece bir anahtar tipinin asla stringsadece olamayacağı içindir. "Minimal" sürüm string | number, dolayısıyla yazım hatasıdır. İlginç bir şekilde, içindeki Tanahtar türünü almaya çalışırken, türün üzerine gelmek setLayerPropstürü hemen görüntülemiyor. Ben de denedim
// Inside the setLayerProps function
type tKey = keyof T;
const asStr: tKey = "43";
const asNum: tKey = 43;
const asObj: tKey = { a: "a" };
Ve şu hatayla sonlandırdım:
Type '{ a: string; }' is not assignable to type 'keyof T'.
Type '{ a: string; }' is not assignable to type 'string | number'.
Type '{ a: string; }' is not assignable to type 'number'.
Yani TypeScript gerçekten de birstring | number .
Nedense henüz anlamıyorum, bu işe yaramıyor:
const key = k as string | number;
Hatalara yol açtığı için:
Type 'number' cannot be used to index type 'T'.(2536)
Type 'string' cannot be used to index type 'T'.(2536)
Açık döküm neden güvenlidir?
In setLayerPropsfonksiyonu, bunu biliyoruz itemanahtar ya bir olduğunu stringya bir number. Yani açıkça döküm
const key = k as keyof T;
Yani , bir dizenin bir dizi veya bir sayı olduğunu söylüyorum , bu benim inancıma göre her zaman doğrudur.