I have an array like:
const arr = ['a', 'b'] as const;
// type: readonly ["a", "b"]
Now I want to join the strings in the array:
const joined = arr.join('');
// type: string
My goal is to write a function that joins strings in an array and returns them as a literal string type ("ab"
).
Does someone know if this is even possible? I think it should be, since all the type information needed is given. I don't expect a full solution to the problem - maybe just a hint for further research would be great.
I have an array like:
const arr = ['a', 'b'] as const;
// type: readonly ["a", "b"]
Now I want to join the strings in the array:
const joined = arr.join('');
// type: string
My goal is to write a function that joins strings in an array and returns them as a literal string type ("ab"
).
Does someone know if this is even possible? I think it should be, since all the type information needed is given. I don't expect a full solution to the problem - maybe just a hint for further research would be great.
Share Improve this question edited Jun 22, 2022 at 22:26 Kostas Minaidis 5,5754 gold badges20 silver badges29 bronze badges asked Jun 22, 2022 at 22:21 nyarthannyarthan 5355 silver badges18 bronze badges 8- 1 Yes, it is possible. – Kostas Minaidis Commented Jun 22, 2022 at 22:28
- You just need to define a function and move all the required operations inside the function body, then return the result. – Kostas Minaidis Commented Jun 22, 2022 at 22:28
-
@KostasMinaidis But how to get the function typed as desired? I'm thinking it might involve a recursive variadic tuple type. Easily doable if the function itself is recursive, but to use
.join
and recurse only in the type is a step beyond. – CertainPerformance Commented Jun 22, 2022 at 22:39 - 3 Something like this? tsplay.dev/NnK8om – spender Commented Jun 22, 2022 at 23:00
-
1
I'm too tired to write this up as an answer, so consider it your hint. Feel free to self-answer with what you learn from this. The type inference can also be improved with TS4.7 by using
extends
constraints oninfer
type variables – spender Commented Jun 22, 2022 at 23:05
5 Answers
Reset to default 1const arr = ['a', 'b'] as const;
type JoinStrings<T extends readonly string[]> = T extends readonly [infer F, ...infer R]
? `${F}${JoinStrings<R>}`
: '';
const joinArray = <T extends readonly string[]>(arr: T): JoinStrings<T> => arr.join('') as JoinStrings<T>;
const joined = joinArray(arr);
Here is a version that works with both readonly and mutable tuples types and a separator.
type Join<
TElements,
TSeparator extends string,
> = TElements extends Readonly<[infer First, ...infer Rest]>
? Rest extends ReadonlyArray<string>
? First extends string
? `${First}${Rest extends [] ? '' : TSeparator}${Join<Rest, TSeparator>}`
: never
: never
: '';
type MutableTuple = ["a", "b", "c"];
type MutableTupleJoined = Join<MutableTuple, " | ">;
// ^? "a | b | c"
type ReadonlyTuple = readonly ["a", "b", "c"];
type ReadonlyTupleJoined = Join<ReadonlyTuple, " | ">;
// ^? "a | b | c"
A readonly type often es from const
assertions e.g.
const tuple = ["a", "b", "c"] as const;
type Joined = Join<typeof tuple, " | ">;
When inlining the tuple type, it is usually mutable e.g.
type Joined = Join<["a", "b", "c"], " | ">;
const joinStrings = <T extends readonly string[]>(arr: T): `${T[number]}` => arr.join('') as `${T[number]}`;
const arr = ['a', 'b'] as const;
const joined = joinStrings(arr); // type: "ab"
try this.
By using as ${T[number]}
, you are telling TypeScript that the return value of arr.join('')
should be treated as a literal string type based on the elements in the array.
This should work.
T extends [infer F, ...infer R] ?
F extends string ?
R extends string[] ?
`${F}${Concat<R>}`
: never
: never
: ""
const alphabets:Concater<["A", "B", "C"]> = "ABC"
const joinedString = (arr as string[]).join('');
console.log(joinedString); // Output: 'ab'
This should work.