我想扩展Array.prototype.join
,这样如果数组是元组,它会使用分隔符返回一个模板化的字符串类型。我有一个实用程序类型可以做到这一点,但无论我如何尝试,当我尝试使用扩展的Array
定义时,this
总是被转换为数组。有人知道如何做到这一点吗?
另外,我很想找到一些高级类型的好资源,比如:书籍、文章、视频等
Playground链接
参考代码:
type Throw<ErrorMessage extends string> = `TypeError: ${ ErrorMessage }` & { [ Key in ErrorMessage ]: void }
type Join<TupleOrArray, Delimiter> =
TupleOrArray extends [] ? ''
: Delimiter extends string
? TupleOrArray extends [infer First, ...infer Rest] | Readonly<[infer First, ...infer Rest]>
? First extends string | number | bigint | boolean | null | undefined
? Rest extends []
? `${ First }`
: `${ First }${ Delimiter }${ Join<Rest, Delimiter> }`
: Throw<'Rest must be an array of strings or empty array'>
: TupleOrArray extends { toString: () => string }[]
? string
: Throw<'StringTuple is not an array or tuple'>
: Throw<'Delimiter must be a string'>
;
interface Array<T> {
join<Delimiter>(delimiter: Delimiter): this extends infer TupleOrArray ? Join<TupleOrArray, Delimiter> : never
}
const array = ['a', 'b', 'c']
const tuple = [ 'a', 'b', 'c' ] as const
type JoinedArray = Join<typeof array, '-'>
// ^? type JoinedArray = string
const joinedArray = array.join('-')
// ^? const joinedArray: string
// 😎
type JoinedType = Join<typeof tuple, '-'>
// ^? type JoinedType = 'a-b-c'
const joinedTuple = tuple.join('-')
// ^? const joinedTuple: string
// 😭
在上面的代码中可以看到,当我希望它是'a-b-c-'时,joinedTuple
只是一个string
。
我错过了什么?
1条答案
按热度按时间mkshixfv1#
当你在一个数组文本上使用
const
Assert时,你会得到一个readonly
元组类型:在尝试merge中的任何内容之前,询问IntelliSense
tuple.join
是什么以及它来自哪里是很有用的:因此,编译器从
ReadonlyArray<T>
接口获取join()
方法,如此处所声明的。这意味着如果你想合并
join()
的重载签名并让它影响tuple
,你应该把它放在ReadonlyArray<T>
而不是Array<T>
中:请注意,我已经修改了该签名,以便将type参数约束为
string
,这样编译器就知道将其推断为字符串类型,而不仅仅是microsoft/TypeScript#10676中所描述的string
。而且我看不出有什么理由要把
this
复制到一个新的类型参数中,所以我把它省略了,如果你需要这样做的话,我想这是可以的,但是它没有在示例代码中显示出来。让我们来测试一下:
看起来不错。
在实践中,你可能不会接触到很多不是
readonly
的字符串常量元组,但是如果你还想为可变数组重载join()
,你也必须在这里显式地这样做:你可以看到它的行动:
Playground代码链接