type Join<P extends string[], D extends string> = P extends [infer R, ...infer rest] ? rest['length'] extends 0 ? R : rest extends string[] ? `${R & string}${D}${Join<rest, D>}` : never : ''
declare function join<D extends string>(delimiter: D): <P extends string[]>(...parts: P) => Join<P, D>
Solution by ouzexi #34253
// 1. 先实现一个类型,这个类型作用将数组`P`中的类型用`D`连接起来
type Join<D extends string, P extends string[]> = P extends [
infer L extends string,
...infer Rest extends string[]
]
? `${L}${Rest extends [] ? "" : D}${Join<D, Rest>}`
: "";
// type Result = Join<'-', ['a', 'b', 'c']> // a-b-c
// 2. Join函数使用泛型 `D` 来推断分割符字面量, 结果返回一个函数,这个函数需要使用泛型 `List` 来推断参数类型。
declare function join<D extends string>(
delimiter: D
): <List extends string[]>(...arg: List) => Join<D, List>;
Solution by Vampirelee #32627
type Joined<T, S extends string> = T extends [
infer F extends string,
...infer L extends string[]
]
? `${F}${L extends [] ? "" : S}${Joined<L, S>}`
: "";
declare function join<S extends string>(delimiter: S): <T extends string[]>(...parts: T) => Joined<T,S>
Solution by vangie #32250
type Join<P extends string[], D extends string> = P extends [infer A extends string, ...infer B]
? B extends [] ? `${A}` : B extends string[] ? `${A}${D}${Join<B, D>}` : never
: ''
declare function join<D extends string>(delimiter: D):
<P extends string[]>(...parts: P) => Join<P, D>;
Solution by chliguiy #31165
type JoinStr<T extends string[], D extends string> = T extends [infer A, ...infer B extends string[]] ? `${A & string}${B['length'] extends 0 ? '' : D}${JoinStr<B, D>}` : ''
declare function join<D extends string>(delimiter: D): <A extends string[]>(...args: A) => JoinStr<A, D>
Solution by dreamluo-plus #30766
type Attach<
Delimiter extends string,
Letters extends string[]
> = Letters extends [infer OnlyItem]
? OnlyItem
: Letters extends [infer CurrentLetter extends string, ...infer Rest extends string[]]
? `${CurrentLetter}${Delimiter}${Attach<Delimiter, Rest>}`
: ''
declare function join<Delimiter extends string>(
delimiter: Delimiter
): <Letters extends string[]>(...parts: Letters) => Letters[0] extends undefined
? ''
: Letters[1] extends string
? Attach<Delimiter, Letters> : Letters[0]
Solution by simone-paglino #29647
type Join<U extends any[], T extends string> =
U extends [string, ...infer R] ? `${U[0]}${R extends [] ? '' : T}${Join<R, T>}` : '';
declare function join<T extends string>(delimiter: T): <U extends string[]>(...parts: U) => Join<U, T>;
Solution by smileboyi #28137
type ReadonlyTuple = (string | number)[]
type Join<T extends ReadonlyTuple, U extends string | number, _Acc extends string = ''> =
T extends [infer Char extends string | number, ...infer Rest extends ReadonlyTuple] ?
Join<Rest, U, `${_Acc}${Char}${Rest extends [] ? '' : U}`> : _Acc
declare function join<T extends string>(delimiter: T):
<S extends ReadonlyTuple>(...parts: S) => Join<S, T>
Solution by jjswifty #27540
// your answers
type GetRes<T extends string, Arg extends string[], Res extends string = ''> = Arg extends [infer L extends string, ...infer R extends string[]] ? R['length'] extends 0 ? `${Res}${L}` : GetRes<T, R, `${Res}${L}${T}`> : Res
declare function join<T extends string>(delimiter: T): <P extends string[]>(...parts: P) => GetRes<T,P>
Solution by Mountain-Z #25887
Many many libraries need functions like this, but I bet you'll be hard pressed to do one that actually implements it in the type system when literals are used. Suddenly, we've hit upon a truly useful challenge.
// ============= Test Cases =============
import type { Equal, Expect } from './test-utils'
const noCharsOutput = join('-')();
type A1 = typeof noCharsOutput;
type B1 = '';
type C1 = Expect<Equal<A1, B1>>;
const oneCharOutput = join('-')('a');
type A2 = typeof oneCharOutput;
type B2 = 'a';
type C2 = Expect<Equal<A2, B2>>;
const noDelimiterOutput = join('')('a', 'b', 'c');
type A3 = typeof noDelimiterOutput;
type B3 = 'abc';
type C3 = Expect<Equal<A3, B3>>;
const twoCharOutput = join('-')('a', 'b');
type A4 = typeof twoCharOutput;
type B4 = 'a-b';
type C4 = Expect<Equal<A4, B4>>;
const hyphenOutput = join('-')('a', 'b', 'c');
type A5 = typeof hyphenOutput;
type B5 = 'a-b-c';
type C5 = Expect<Equal<A5, B5>>;
const hashOutput = join('#')('a', 'b', 'c');
type A6 = typeof hashOutput;
type B6 = 'a#b#c';
type C6 = Expect<Equal<A6, B6>>;
const longOutput = join('-')(
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'
);
type A7 = typeof longOutput;
type B7 = 'a-b-c-d-e-f-g-h';
type C7 = Expect<Equal<A7, B7>>;
// ============= Your Code Here =============
// @uid11
type Tuple = readonly string[];
/**
* Tail<['1', '2', '3']> = ['2', '3'].
*/
type Tail<T extends Tuple> =
T extends readonly [unknown, ...infer Rest]
? Rest
: [];
/**
* Join<['1', '2'], " - "> = '1 - 2'.
* Join<['1'], " - "> = '1'.
* Join<[], 'x'> = ''.
*/
type Join<
T extends Tuple,
Separator extends string
> =
// handle empty tuple
T extends readonly []
? ''
// handle tuple with one element
: T extends readonly [infer Head]
? Head
: `${T[0]}${Separator}${Join<Tail<T>, Separator>}`;
declare function join<
D extends string
>(delimiter: D):
<P extends Tuple>(...parts: P) => Join<P, D>;
// ============== Alternatives ==============
// @zongc1001
type Join<
Parts extends readonly string[],
Delimiter extends string = ''
> =
Parts extends [
infer Head,
...infer Tail extends readonly string[]
]
? Head extends string
? Tail extends []
? Head
: `${Head}${Delimiter}${Join<Tail, Delimiter>}`
: never
: '';
declare function join<Delimiter extends string>(
delimiter: Delimiter
): <Parts extends readonly string[]>(
...parts: Parts
) => Join<Parts, Delimiter>;
For more video solutions to other challenges: see the umbrella list! https://github.com/type-challenges/type-challenges/issues/21338
Solution by dimitropoulos #25350
declare type Join<T extends string, U extends string[]> = U extends [infer F extends string, ...infer R extends string[]] ? (R extends [] ? F : `${F}${T}${Join<T, R>}`) : ``;
declare function join<T extends string>(delimiter: T): <U extends string[]>(...parts: U) => Join<T, U>;
Solution by E-uler #24904
type Join<T extends string[], U extends string, Acc extends string = ''> = T extends [infer First extends string, ...infer Rest extends string[]]
? Rest extends []
? `${Acc}${First}`
: Join<Rest, U,`${Acc}${First}${U}`>
: Acc
declare function join<T extends string>(delimiter: T): <S extends string[]>(...parts: S) => Join<S, T>
Solution by NeylonR #24449
type Join<P extends string[], D extends string> =
P extends [infer P0 extends string, ...infer PRest extends [string, ...string[]]] ? (
`${P0}${D}${Join<PRest, D>}`
) : P[0]
declare function join<D extends string>(delimiter: D): <P extends string[]>(...parts: P) =>
P extends [] ? '' : Join<P, D>
Solution by BOCbMOU #24196
type StringJoin<
S extends string[],
T extends string,
Acc extends string = ''
> = S extends [infer F extends string, ...infer R extends string[]]
? R extends []
? `${Acc}${F}`
: StringJoin<R, T, `${Acc}${F}${T}`>
: Acc;
declare function join<T extends string>(delimiter: T): <S extends string[]>(...parts: S) => StringJoin<S, T>
Solution by snakeUni #23735
type Join<T extends string[], By extends string = '', Result extends string = ''> = T extends [
infer F,
...infer R
]
? R extends string[]
? Join<R, By, F extends string ? `${Result}${Result extends '' ? Result : By}${F}` : Result>
: Result
: Result
Solution by TKBnice #23115
// your answers
type StringJoin<
S extends string[],
T extends string,
R extends string = ''
> = S extends [infer F extends string, ...infer Rest extends string[]]
? Rest extends []
? `${R}${F}`
: StringJoin<Rest, T, `${R}${F}${T}`>
: R;
declare function join<T extends string>(delimiter: T): <S extends string[]>(...parts: S) => StringJoin<S, T>
Solution by jxhhdx #22857
type StringJoin<S extends string[], D extends string, R extends string = ''> =
S extends [infer A extends string, ...infer Rest extends string[]]
? StringJoin<Rest, D, `${R}${R extends '' ? '' : D}${A}`>
: R
declare function join<D extends string> (
delimiter: D
): <S extends string[]>(...parts: S) => StringJoin<S, D>
Solution by drylint #22329
type JoinStr<T extends string, U extends string[], P extends string = ""> =
U extends [infer F extends string, ...infer R extends string[]]
? JoinStr<T, R, `${P}${P extends "" ? "" : T}${F}`>
: P;
declare function join<T extends string>(delimiter: T): <U extends string[]>(...parts: U) => JoinStr<T, U>;
Solution by ivbrajkovic #22257
type StringJoin<T extends string, U extends string[]> = U extends [infer First extends string, infer Second extends string, ...infer Rest extends string[]] ?
`${First}${T}${StringJoin<T,[Second, ...Rest]>}` : U[0] extends undefined ? '' : U[0];
declare function join<T extends string>(delimiter: T): <U extends string[]>(...parts: U) => StringJoin<T,[...U]>;
Solution by Karamuto #22029
type JoinChars<Chars extends string[] = [], JoinChar extends string = ''> =
Chars['length'] extends 1
? Chars[0]
: Chars extends [infer First extends string, ...infer Rest extends string[]]
? `${First}${JoinChar}${JoinChars<Rest, JoinChar>}`
: '';
declare function join<JoinChar extends string>(
joinChar?: JoinChar
): <Chars extends string[]>(...params: Chars) => JoinChars<Chars, JoinChar>;
const hyphenOutput2 = join('')('a', 'b', 'c');
const oneCharOutput = join('-')('a');
const hyphenOutput = join('-')('a', 'b', 'c');
const hashOutput = join('#')('a', 'b', 'c');
const twoCharOutput = join('-')('a', 'b');
const longOutput = join('-')('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h');
const noCharsOutput = join('-')();
Solution by zqiangxu #21704
type Join<D extends string, P extends string[]> =
P extends []
? ''
: P extends [infer First]
? First
: P extends [infer First extends string, ...infer Rest extends string[]]
? `${First}${D}${Join<D, Rest>}`
: ''
declare function join<D extends string>(delimiter: D): <P extends string[]>(...parts: P) => Join<D, P>
key point for me: <P extends string[]>(...parts: P) => ...
I should put generic type before second function's argument bracket.
Solution by zhaoyao91 #21310
type Join<T extends Array<string>, U extends string | number> = T extends [
infer H extends string,
...infer L extends string[]
]
? `${H}${L extends [] ? "" : U}${Join<L, U>}`
: "";
declare function join<T extends string>(
delimiter: T
): <R extends string[]>(...parts: R) => Join<R, T>;
Solution by so11y #21228
// your answers
type DeepJoin<T extends string,S> = S extends [infer F,...infer R] ? F extends string ? R extends [string,...infer R1] ?
`${F}${T}${DeepJoin<T,R>}` : `${F}` : never : ''
declare function join<T extends string>(delimiter: T): <S extends string[]>(...parts: S) => DeepJoin<T,S>
Solution by YqxLzx #21146
// your answers
type JoinStr<D extends string, Vals extends any[], Ret extends string = ''> = Vals extends [infer First extends string,...infer Rest extends string[]] ? Rest extends readonly [] ? `${Ret}${First}`: JoinStr<D, Rest, `${Ret}${First}${D}`>: Ret
// declare function join<D extends string>(delimiter: D): <Vals extends any[]>(...parts: Vals) => JoinStr<D, Vals> // 没有想明白为什么 Vals 为 any[] 的时候,推导出来的时 string 而不是字面量类型? 有 大佬可以帮忙解释下吗 ? type rr = JoinStr<'#', ['a', 'b', 'c']> 这个却是能够正常的推导出来
declare function join<D extends string>(delimiter: D): <Vals extends string[]>(...parts: Vals) => JoinStr<D, Vals>
Solution by Rebornjiang #20709
type Joined<TDel extends string, TVals> = TVals extends [infer T1 extends string, ...infer TR]
? `${T1}${TR extends [] ? '' : TDel}${Joined<TDel, TR>}`
: '';
declare function join<TDel extends string>(delimiter: TDel): <TVals extends string[]>(...parts: [...TVals]) => Joined<TDel, TVals>;
Solution by mdakram28 #20654
declare function join<SEP extends string>(delimiter: SEP): <T extends string[]>(...parts: T) => Join<T, SEP>
type Join<T extends string[], SEP extends string> = T extends
[infer F extends string, ...infer O extends string[]]
? `${F}${O extends [] ? '' : `${SEP}${Join<O, SEP>}`}`
: ''
Solution by pengzhanbo #20479
type Join<T extends string, K extends string[], F extends boolean = true> = K extends [infer L extends string, ...infer R extends string[]]
? `${F extends true ? '' : T}${L}${Join<T, R, false>}`
: ''
declare function join<T extends string>(delimiter: T): <K extends string[]>(...parts: K) => Join<T, K>
Solution by chhu1 #20452
type JoinString<T extends string[], TSep extends string> =
T extends [infer TFirst extends string, ...infer TRest extends string[]]
? `${TFirst}${TRest extends [] ? '' : `${TSep}${JoinString<TRest, TSep>}`}`
: ''
declare function join<T extends string>(sep: T) : <TArgs extends string[] = []>(...args: TArgs) => JoinString<TArgs, T>
Solution by knazeri #19118
type JoinString<A extends string[], T extends string, Result extends string = ''> = A extends [infer F extends string, ...infer R extends string[]]
? JoinString<R, T, `${Result}${Result extends '' ? '' : T}${F}`>
: Result;
declare function join<T extends string>(delimiter: T): <A extends string[] = []>(...parts: A) => JoinString<A, T>;
Solution by CaoXueLiang #19061
// your answers
type JoinString<Args extends string[] = [], D extends string = '', Result extends string = ''> = Args extends [infer R, ...infer U]
? U extends string[]
? R extends string
? JoinString<U, D, `${Result extends '' ? `${Result}` : `${Result}${D}`}${R}`>
: JoinString<U, D, `${Result}`>
: `${Result}`
: Result
declare function join<T extends string>(delimiter: T): <Arr extends string[]>(...parts: Arr) => JoinString<Arr, T>;
Solution by jiaaoMario #17414