TypeScript - Demet çiftleri dizileri için tür çıkarımı
Aldığı her çift için güvenli bir şekilde bireysel türleri çıkarabilmesi için create fonksiyonumu nasıl yazabilirim?
type Component<T=any> = { id: string, _type?: T };
type Pair<T=any> = [Component<T>, T];
function create<T extends Pair[]>(...pairs: T) {
// ...
}
type Vector = [number, number];
type Status = "active" | "idle";
let Position: Component<Vector> = { id: "position" };
let Status: Component<Status> = { id: "status" };
let entity = create(
[Position, [0, 0]],
[Status, false], // should fail as it expects `Status` and got `boolean`
);
Oyun alanı sürümü
Tür parametresini açıkça geçirerek istenen davranışı elde edebilirim.
let entity = create<[
Pair<Vector>,
Pair<Status>,
]>(
[Position, [0, 0]],
[Status, false], // should fail as it expects `Status` and got `boolean`
);
Buradaki sezgim, derleyicinin, pairs parametresini zaten Pair[] türünde çıkarsadıktan sonra, iç içe Pair türlerini elemanlarından çıkarmaya çalışmayacağı yönünde.
Cevap #1
Bir dizinin öğesi içinde böyle bir ilişki elde etmek TS'de çok doğrudan değildir. Başka bir cevapta belirtildiği gibi çözümlerden biri, bir çift için özel bir işlev oluşturmak ve bunu her öğede çağırmaktır. Bu çözüm işe yarasa da, kullanıcının bu kontrolü manuel olarak devreye sokmasını zorunlu kıldığı için ideal değildir.
Başka bir çözüm, kontrolü gerçekleştirmek için özel bir yol kullanmaktır. Bu, aşağıdakilere dayanır:
Eğer bir parametre, bir tür parametresi ve o tür parametresine bağlı ikinci bir tür içeren bir kesişimse (örnek: T & SomeMappedType<T>), TypeScript tür parametresi T’yi türetecek ve türetilmiş türü SomeMappedType<T> içinde kullanacak ve parametreyi SomeMappedType<T> ile kontrol edecektir. Temelde parametrenin gerçek türünü T içinde yakalıyoruz, ancak kontrolü T’ye bağlı ayrı bir tür üzerinden gerçekleştiriyoruz.
Bu örnek için bunun anlamı, dizinin gerçek türünü bir demet olarak yakalamak için bir tür parametresi kullanacağımızdır (örneğinizde: [[Component<Vector>, number[]], [Component<Status>, boolean]]) ve ardından kontrol etmek istediğimiz türü oluşturmak için bu türü kullanacağız (bu örnekte [Pair<Vector>, Pair<Status>] olmalıdır). Bunu yapmak için basit bir eşlenmiş tür (mapped type) kullanacağız.
type Component<T> = { id: string, usage?: T };
type Pair<T> = [Component<T>, T];
type PairCheck<T extends Pair<any>[]> = {
[P in keyof T]: T[P] extends [Component<infer U>, any] ? Pair<U>: never
}
function create<T extends Pair<any>[]>(...pairs: T & PairCheck<T>) {
// ...
}
type Vector = [number, number];
type Status = "active" | "idle";
let Position: Component<Vector> = { id: "position" };
let Status: Component<Status> = { id: "status" };
let entity = create(
[Position, [0, 0]],
[Status, true],
);
Oyun Alanı Bağlantısı
Not: Component'in herhangi bir şekilde çalışabilmesi için T kullanması GEREKİR. Kullanılmayan tip parametreleriyle ilgili SSS'i okuyun.
Aldığınız hata şudur
Argument of type '[[Component<Vector>, [number, number]], [Component<Status>, boolean]]' is not assignable to parameter of type '[[Component<Vector>, number[]], [Component<Status>, boolean]] & [Pair<Vector>, Pair<Status>]'.
Type '[[Component<Vector>, [number, number]], [Component<Status>, boolean]]' is not assignable to type '[Pair<Vector>, Pair<Status>]'.
Type '[Component<Status>, boolean]' is not assignable to type 'Pair<Status>'.
Type 'boolean' is not assignable to type 'Status'
Bu uzun bir ifade ve hata aralığı ne yazık ki sorunlu olan değil ilk öğe üzerinde (ki bu aslında bir derleyici hatası olabilir), ama bence neyin yanlış olduğunu net bir şekilde gösteriyor.
Cevap #2
create içindeki dinlenme parametreniz ...pairs öğelerinin tümü, Pair[]'yi genişleten ortak bir türe paketlenir (varsayılan durumda T any olarak çözülür). Derleyici verilen argümanları bir dizi olarak yorumlar ve dizideki öğelerin türü aynıdır, bu yüzden burada tür açıklamaları olmadan bireysel öğeler için tür kontrolü yapmak mümkün değildir.
Bir alternatif, create için jenerik tür parametresini elle yazarak sağladığınız örnek veya fonksiyon argümanlarını fonksiyon çağrısından ayırmaktır:
const t: [Pair<Vector>, Pair<Status>] = [[Position, [0, 0]], [Status, false]]; // error (OK)
let entity = create(...t);
Bu çözüm, create içinde geçilecek sabit sayıda Pair öğesi olduğunda veya tüm Pair öğelerinin bileşik türü bilindiğinde uygundur. Bu mümkün olmadığında ve/veya dizi daha dinamik bir şekilde oluşturulduğunda, tek amacı Pair öğelerinin güçlü tiplemesini zorunlu kılmak olan asPair adlı bir yardımcı fonksiyon kullanabilirsiniz, şöyle ki:
function asPair<T>(t: Pair<T>) {
return t;
}
let entity = create(
asPair([Position, [0, 0]]),
asPair([Status, false]) // error: Type 'false' is not assignable to type 'Status'. (OK)
);
Oyun alanı
