I have a type that looks something like this:
type Location=`${number},${number};${number},${number};...`
Is there a Utility type like Repeat<T>
that can do this for me?
like this:
type Location=Repeat<`${number},${number};`>
I have a type that looks something like this:
type Location=`${number},${number};${number},${number};...`
Is there a Utility type like Repeat<T>
that can do this for me?
like this:
type Location=Repeat<`${number},${number};`>
IT WORKS ONLY IN TS >=4.5
It is possible to create standalone type.
Please see this example:
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
types is an utility type which represents a while
loop. It iterates until length of Result
will reach N
. In other words Mapped<10>
- will iterate 10 times.
See this example in pure 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};`])
}
It is hard to represent unions in js, thats why I have used join('')
. I hope it is clear how it works.
If you want to increase MAXIMUM_ALLOWED_BOUNDARY
to 500 it will heat your CPU so be careful.
As you might have noticed, it is impossible in type script to represent recursive pattern for type but it is possible to create big enough union.
Please keep in mind that there are some drawbacks of ${number}
type. You are allowed to use numbers with leading zero like here:
const x: `${number}` = '01'.
I don't think there is a way to define an infinite repeating pattern for a type that you use on variable declaration.
However, a type guard on a function can check that a string matches an infinite pattern, like so (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;"); // ⚠️
You can directly choose type string
if you want as:
type myLocation = string;
const myLocation: myLocation = "123123123";
console.log(myLocation);
else if you want to narrow it down then better to declare it const
as:
const myLocation = "123123123";
console.log(myLocation);
then your type will be the value of the myLocation
I borrowed this from my other TS challenge where I try to mix console colors (gist), it detects unlimited repetitions, but I haven't figured out how to practically apply it as a guard. So this is more like a fun exercise.
type ValidCodes = `${number}` //"0" | "1" | "2" | "31" | "32" | "33"
type Tail<T> = T extends [infer _FirstItem, ...infer Rest] ? Rest : never
type Merge<A extends ValidCodes[]> = Tail<A>['length'] extends 0
? A[0]
// @ts-expect-error Type 'DotMergeTuple<Tail<A>>' is not assignable to type 'string | number | bigint | boolean'.
: `${A[0]};${Merge<Tail<A>>}`
type Split<T> = T extends `${infer A};${infer B}`
? [A, ...Split<B>]
: [T]
type T01 = Merge<['1', '1', '33', '31']>
type T02 = Split<'0;1;2;31;32'>
type T = Merge<Split<'0;1;2;31;33;x'>> // Type "x" is not assignable to type `${number}`.
You can use an as const
assertion to get a string literal type from a template literal, like this:
const str1 = 'str1';
const str2 = `${str1},${str1};${str1},${str1}` as const; // type is "str1,str1;str1,str1"