Reputation: 1499
I have a use-case where I have a bunch of functions which have to meet certain criteria, i.e:
string
and an Array<K>
unknown
, any
, etc. because it will override the type inferenceIn essence, I'm looking for some magic type like:
type MagicType<K> = (str: string, keys: Array<K>) => Promise<infer>;
so that these functions would type-check: and return inferred type:
const f1: MagicType<number> = async (str, keys) => {
return '42'
}
// expecting typeof f1 === (string, Array<number>) => Promise<string>
const f2: MagicType<number> = async (str, keys) => {
return keys.length;
}
// expecting typeof f2 === (string, Array<number>) => Promise<number>
// note lack of `async` keyword
const f_error2: MagicType<number> = (str, keys) => {
return keys.length;
}
// expecting to be a compilation error, since number is not Promise<unknown>
My trials and (only) failures can be found here: TS-playground
Any help appreciated. I know this is most likely a corner-case, but I'd like to know if it's possible. Many thanks!
Upvotes: 1
Views: 221
Reputation: 33051
I see two options og handling this case:
const f1: MagicType<number, string> = async (str, keys) => '42'
type MagicType<K> = (str: string, keys: Array<K>) => Promise<any>;
const infer = <Cb extends MagicType<number>>(cb: Cb) => cb
const infered = infer(async (str, keys) => '42')
const result = infered('hello', [1, 2, 3]) // Promise<string>
Explanation:
infer
- expects one argument cb
. I have created extra Cb
generic parameter to infer cb
argument as much as possible
. Once you create a generic for function argument, TS will try to infer it. If you are interested in type inference on function arguments please check my article
Please keep in mind that once you have provided explicit type MagicType<number>
for function in :
const f1: MagicType<number> = async (str, keys) => {
return '42'
}
TypeScript don't do any inference work it just uses MagicType<number>
.
Consider this example:
const arr: ReadonlyArray<string> = ['a', 'b'] as const;
You may think that TS is able to figure out that apart from being ReadonlyArray<string>
arr
is also ['a','b']
, but it is not true. Array arr
is just a ReadonlyArray<string>
it is no more possible to infer literal type of array element.
UPDATE
You can also use :
type MagicType<K> = (str: string, keys: Array<K>) => Promise<unknown>;
const infer = <Type, Cb = MagicType<Type>>(cb: Cb) => cb
const infered = infer<string>(async (str, keys) => '42')
Explanation:
infer
- function expects two generic parameters:
Type
, represents type of element in the array keys
. It refers to K
in your example.Cb
has default parameter, it means that you don't need to provide second parameter to your infer
fucntion. Second parameter Cb
is just a MagicType<Type>
. Hence, just like in your example, you need to provide type of keys
element.We still don't have any geeric for return type of Cb
. We don't need, since TS is able to infer it for us.
You can also replace Promise<any>
with Promise<unknown>
P.S. I'm not a big fan of using explicit
generics.
This case is not strictly related to our problem, but it might be helpful for you:
Consider this very simple example:
const returnType = <Return,>(cb: () => Return) => cb()
const result = returnType(() => 42) // number
TS is able to infer Return
generic
Upvotes: 2