如何定义具有重复结构的 typescript 类型?

y53ybaqx  于 2023-06-07  发布在  TypeScript
关注(0)|答案(5)|浏览(294)

我有一个类型,看起来像这样:

type Location=`${number},${number};${number},${number};...`

有没有像Repeat<T>这样的实用程序类型可以为我做到这一点?就像这样:

type Location=Repeat<`${number},${number};`>
r55awzrz

r55awzrz1#

仅适用于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

Playground
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,它会加热你的CPU,所以要小心。
正如你可能已经注意到的,在类型脚本中不可能表示类型的递归模式,但可以创建足够大的联合。
请记住,${number}类型有一些缺点。你可以使用带前导零的数字,如下所示:

const x: `${number}` = '01'.

有用链接

  1. Here你可以找到一个例子,你可以如何使用这个模式创建一个数字范围。
  2. Here您可以找到引入此功能的PR
  3. Here,在这里你可以找到我的文章,其中涉及到这个模式
nhhxz33t

nhhxz33t2#

我不认为有一种方法可以为变量声明中使用的类型定义无限重复模式。
然而,函数上的类型保护可以检查字符串是否匹配无限模式,就像这样(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;"); // ⚠️
waxmsbnn

waxmsbnn3#

您可以直接选择string类型,如果您想要:

type myLocation = string;
const myLocation: myLocation = "123123123";

console.log(myLocation);

否则,如果你想缩小它的范围,那么最好将它声明为const

const myLocation = "123123123";
console.log(myLocation);

那么你的类型将是myLocation的值

zfciruhq

zfciruhq4#

我从我的另一个TS挑战中借用了这个,我试图混合控制台颜色 (要点),它检测到无限的重复,但我还没有弄清楚如何实际应用它作为一个后卫。所以这更像是一个有趣的练习。

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}`.
whitzsjs

whitzsjs5#

你可以使用as constAssert从模板文本中获取字符串文本类型,如下所示:
TSPlayground

const str1 = 'str1';
const str2 = `${str1},${str1};${str1},${str1}` as const; // type is "str1,str1;str1,str1"

相关问题