TypeScript: Tür örneklemesi aşırı derin ve muhtemelen sonsuz
TypeScript'te, bir literal string'i birden fazla ayırıcı kullanarak bölebilen genel bir split tipi uygulamaya çalışıyorum.
type Spl<T extends string, D extends string> = T extends `${infer A}${D}${infer B}` ?
A extends '' ? [] :
[A, ...Spl<B, D>] : (T extends '' ? [] : [T]);
type SplMany<T extends string, D extends string[]> = D extends [infer H extends string, ...infer Tail extends string[]]
?
T extends '' ? [] : SplManyInputsManyDelimieter<[...Spl<T, H>], Tail>
: [T];
type SplManyInputs<T extends string[], D extends string> = T extends [infer H extends string, ...infer Tail extends string[]]
? [...Spl<H, D>, ...SplManyInputs<Tail, D>] : [];
type SplManyInputsManyDelimieter<T extends string[], D extends string[]> =
D extends [infer H extends string, ...infer Tail extends string[]] ?
SplManyInputsManyDelimieter<[...SplManyInputs<T, H>], Tail>
: T;
type t1 = Spl<'1 2 3 4 5 6 7 8 9 10 11 %test% 12 13 14 15 16 17 18 19 20 21 22 23 24 25 1 2 3 4 5 6 1 2 3 4 5 6 1 2 3 4 5 6 1 2 3 4 1 2 3 4 5 6 7 8 9 10 11 %test% 12 13 14 15 16 17 18 19 20 21 22 23 24 25 1 2 3 4 5 6 1 2 3 4 5 6 1 2 3 4 5 6 1 2 3 4 1 2 3 4 5 6 7 8 9 10 11 %test% 12 13 14 15 16 17 18 19 20 21 22 23 24 25 1 2 3 4 5 6 1 2 3 4 5 6 1 2 3 4 5 6 1 2 3 4 1 2 3 4 5 6 7 8 9 10 11 %test% 12 13 14 15 16 17 18 19 20 21 22 23 24 25 1 2 3 4 5 6 1 2 3 4 5 6 1 2 3 4 5 6 1 2 3 4 1 2 3 4 5 6 7 8 9 10 11 %test% 12 13 14 15 16 17 18 19 20 21 22 23 24 25 1 2 3 4 5 6 1 2 3 4 5 6 1 2 3 4 5 6 1 2 3 4', ' '>;
type t1_ = Spl<'1 ', ' '>;
type t1__ = Spl<'1', ' '>;
type t1___ = Spl<'', ' '>;
type t3 = SplManyInputs<[' 1 2 3 ', ' 3 4 ', '5 6'], ' '>;
type t3_ = SplManyInputs<[], ' '>;
type t4 = SplManyInputsManyDelimieter<['1 2', '3 4', '5 6'], [' ']>;
type t4_ = SplManyInputsManyDelimieter<['1 2', '3 4', '5 6'], []>;
type t4__ = SplManyInputsManyDelimieter<[], [' ']>;
type test = SplMany<'0_1 0_2 0_3 0_4 0_5 0_6 0_7 0_8 0_9 1_0 1_1 1_2 1_3 1_4 1_5 1_6 1_7 1_8 1_9 2_0 2_1 2_2 2_3 2_4 2_5 2_6 2_7 2_8 2_9 3_0 3_1 3_2 3_3 3_4 3_5 3_6 3_7 3_8 3_9 4_0 4_1 4_2 4_3 4_4 4_5 4_6 4_7 4_8', [' ', '_']> // error!
// Type instantiation is excessively deep and possibly infinite.
TS oyun alanı bağlantısını gör
Kod çalışıyor ama girdi yeterince uzun olduğunda, Type instantiation is excessively deep and possibly infinite. ts(2589) hatasını alıyorum. Sanırım bu şekilde bölmek çok verimli değil ama yine de özyineleme sınırının biraz daha yüksek olmasını bekliyordum.
Daha uzun girdilere izin vermek için kodu optimize edebileceğim herhangi bir hile var mı? Ayrıca hata mesajından sorumlu olan TypeScript derleyicisinin kısmını bulmaya çalıştım ama bulamadım. Hatanın nerede verildiğini bilen var mı?
Cevap #1
Derinlemesine bir özyinelemeli koşullu tür yazdığınızda ve özyinelemenin birkaç düzine seviyeden fazla inmesini amaçladığınızda, özyineleme sınırlarıyla karşılaşabilir ve "tür örneklemesi aşırı derin ve muhtemelen sonsuz" hata mesajını alabilirsiniz.
Böyle durumlarda, sıklıkla microsoft/TypeScript#45711'de uygulandığı şekilde koşullu tiplerde kuyruk özyineleme eliminasyonu kullanacak şekilde yeniden yapılandırabilir ve bin seviyeye kadar özyinelemeye ulaşabilirsiniz, bu da çoğu amaç için genellikle yeterince iyidir.
Yaklaşım, türün her türlü özyinelemeli kullanımının kuyruk özyinelemeli (tail-recursive) olmasını sağlamaktır; yani özyinelemeli türün sonucu doğrudan çıktı olarak verilir ve ek bir işlem gerektirmez. Genellikle bu, tür fonksiyonunuza ekstra bir biriktirici tür parametresi ekleyerek yapılabilir. Spl ve SplManyInputs için bu muhtemelen şöyle görünebilir:
type Spl<T extends string, D extends string, ACC extends string[] = []> =
T extends `${infer A}${D}${infer B}` ? A extends '' ? ACC :
Spl<B, D, [...ACC, A]> : (T extends '' ? ACC : [...ACC, T]);
type SplManyInputs<T extends string[], D extends string, ACC extends string[] = []> =
T extends [infer H extends string, ...infer Tail extends string[]]
? SplManyInputs<Tail, D, [...ACC, ...Spl<H, D>]> : ACC;
Her iki durumda da nihai çıktıyı tutmak için bir ACC türü parametre oluşturdum; bu parametre başlangıçta boş demet [] olarak başlıyor ve kademeli olarak ara sonuçlarla dolduruluyor. Temel durum ise sadece toplayıcıyı geri döndürmek.
Bunun, sizin sürümlerinizle aynı sonuçları verdiğini doğrulayabilirsiniz, ancak verilen örnekler için özyineleme sınırlarına takılmaz.
Koda yönelik oyun alanı bağlantısı
