javascript - איך אני מגדיר טיפוס TypeScript עם מבנה חוזר?
יש לי טיפ שמראה משהו כזה:
type Location=`${number},${number};${number},${number};...`
האם יש סוג Utility כמו Repeat<T> שיכול לעשות את זה עבורי? ככה:
type Location=Repeat<`${number},${number};`>
תשובה #1
זה עובד רק ב-TS >=4.5
אפשר ליצור סוג עצמאי.
אנא ראה את הדוגמה הזו:
type Coordinates = `${number},${number};`
type MAXIMUM_ALLOWED_BOUNDARY = 50
type Last<T extends string[]> = T extends [...infer _, infer Last] ? Last : never;
type ConcatPrevious<T extends any[]> = Last<T> extends string ? `${Last<T>}${Coordinates}` : never
type Mapped<
N extends number,
Result extends Array<unknown> = [Coordinates],
> =
(Result['length'] extends N
? Result
: Mapped<N, [...Result, ConcatPrevious<Result>]>
)
// type MyLocation =
// | `${number},${number};`
// | `${number},${number};${number},${number};`
// | `${number},${number};${number},${number};${number},${number};`
// | `${number},${number};${number},${number};${number},${number};${number},${number};`
// | `${number},${number};${number},${number};${number},${number};${number},${number};${number},${number};`
// | ... 44 more ...
// | `${number},${number};${number},${number};${number},${number};${number},${number};${number},${number};${number},${number}; ....
type MyLocation = Mapped<MAXIMUM_ALLOWED_BOUNDARY>[number]
const myLocation1: MyLocation = '45,56;67,68;' // ok
const myLocation2: MyLocation = '45,56;67,68;1,2;3,4;5,6;7,8;9,10;' // ok
const myLocation3: MyLocation = '45,56;67,68;1,2;3,4;5,6;7,8;9,10,' // expected error
מִגרָשׂ מְשָׂחָק
סוגי Mapped הם סוג עזר המייצג לולאת while. היא חוזרת עד שאורך Result יגיע ל-N. במילים אחרות Mapped<10> - תחזור 10 פעמים. ראה דוגמה זו ב-JS טהור:
const mapped = (N: number, Result: any[] = []): string => {
if (N === Result.length) {
return Result.join('')
}
const x = Math.random();
const y = Math.random()
return mapped(N, [...Result, `${x},${y};`])
}
קשה לייצג איגודים ב-JS, לכן השתמשתי ב-join(''). אני מקווה שזה ברור איך זה עובד.
אם אתה רוצה להגדיל את MAXIMUM_ALLOWED_BOUNDARY ל-500 זה יחמם את המעבד שלך אז תהייה זהיר.
כפי שאולי שמת לב, זה בלתי אפשרי ב-TypeScript לייצג תבנית רקורסיבית עבור סוג, אבל אפשר ליצור איגוד גדול מספיק.
אנא שים לב שיש כמה חסרונות לסוג $ {number}. מותר להשתמש במספרים עם אפס מוביל כמו כאן:
const x: `${number}` = '01'.
קישורים שימושיים:
כאן תוכלו למצוא דוגמה כיצד תוכלו ליצור טווח של מספרים בעזרת דפוס זה.
כאן תוכלו למצוא PR שבו התכונה הזו הוצגה
כאן וכאן ניתן למצוא את המאמרים שלי הקשורים לתבנית זו
תשובה #2
אני לא חושב שיש דרך להגדיר תבנית חוזרת אינסופית עבור סוג שאתה משתמש בו בהכרזת משתנה.
עם זאת, שומר סוג על פונקציה יכול לבדוק שמחרוזת תואמת לתבנית אינסופית, כך (playground):
type MatchesPattern<Pattern extends string, Current extends string> = Current extends `` ? string : (Current extends `${Pattern}${infer Rest}` ? MatchesPattern<Pattern, Rest> : never);
type LocationPattern = `${number},${number};`;
declare function onlyAcceptsLocation<L extends string & IsLocation, IsLocation = MatchesPattern<LocationPattern, L>>(location: L): void;
onlyAcceptsLocation("12,34;56,78;"); // 👍 matches
onlyAcceptsLocation("12,34;56,78"); // ⚠️
onlyAcceptsLocation("12'34;56,78;"); // ⚠️
onlyAcceptsLocation("1,2,3;45,67;"); // ⚠️
