Issue
I have the following type in typescript:
type wrapper<A extends Array<any> = []> = {
chain<T extends string>(key: T): wrapper<[...A, T]>;
x: A;
};
Inspired by the following challenge: https://github.com/type-challenges/type-challenges/blob/master/questions/12-medium-chainable-options/README.md
The idea is I would be able to do something like this:
let tmp = wrapper.chain('a').chain('b'); // Expected type wrapper<["a", "b"]>
However I am unable to implement the function definition, since it is meant to return a type of wrapper with a function definition for chain, that also needs to return a function etc... Is it possible to implement this type in javascript with typesafety?
Solution
If you want to use functions and plain objects, the easiest thing to do is write a generic function (let's call it w()) that takes a value value of some string[] subtype A and produces a Wrapper<T>:
type Wrapper<A extends string[] = []> = {
value: A;
chain<T extends string>(key: T): Wrapper<[...A, T]>;
};
function w<A extends string[]>(value: A): Wrapper<A> {
return {
value,
chain(key) {
return w([...value, key]);
}
}
}
This is seen as type safe, because the compiler understands that a spread like [...value, key] will produce a variadic tuple type like [...A, T] when T is the type of key. Then wrapper is just w([]) where you pass in the empty tuple of type []:
const empty: [] = []
const wrapper: Wrapper = w(empty);
const val = wrapper.chain("a").chain("b").chain("c").value;
// const val: ["a", "b", "c"]
console.log(val); // ["a", "b", "c"]
If you don't want to expose w and empty, you can use modules or namespaces where you just export wrapper, or you could write an immediately invoked function expression to get the same effect:
const wrapper: Wrapper = (() => {
function w<A extends string[]>(value: A): Wrapper<A> {
return {
value,
chain(key) {
return w([...value, key]);
}
}
}
const empty: [] = []
return w(empty)
})();
Or even more simply:
const wrapper: Wrapper = (function w<A extends string[]>(value: A): Wrapper<A> {
return {
value,
chain(key) {
return w([...value, key]);
}
}
})([]);
Also note that if you're not careful, the value [] will be interpreted by the compiler as the type never[] instead of the tuple type []. One way to discourage that is to replace the array type A with a variadic tuple type [...A]; it means basically the same thing, but it gives the compiler a hint that you want a tuple type:
const wrapper = (function w<A extends string[]>(value: [...A]): Wrapper<A> {
return {
value,
chain(key) {
return w([...value, key]);
}
}
})([]);
Answered By - jcalz
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.