问题
How to safely type curried functions in typescript? With particular regard to the following example
interface Prop {
<T, K extends keyof T>(name: K, object: T): T[K];
<K>(name: K): <T>(object: T) => /* ?? */;
}
const prop: Prop = (key, object) => object[key];
const valid1 = prop('foo', { foo: 'hello' }); // string
const valid = prop('foo')({ foo: 'hello' }); // string
// `never`, since `baz` does not exist in { foo: string }
const invalid = prop('baz')({ foo: 'hello' }); // never
回答1:
function overload
function prop<T, K extends keyof T>(name: K, obj: T): T[K]
function prop<K extends PropertyKey>(name: K):
<T extends Record<K, unknown>>(obj: T) => T[K]
function prop(name: any, obj?: any) {
if (obj === undefined) {
return (obj: any) => obj[name]
} else {
return obj[name]
}
}
// weak types used in impl for simplicity, as they don't matter for the caller.
// also this function body is not really complex
const valid1 = prop('foo', { foo: 'hello1' }); // string
const valid2 = prop('foo')({ foo: 'hello2' }); // string
const invalid = prop('baz')({ foo: 'hello' }); // compile error, `baz` not in { foo: string }
Sample
function type
interface Prop {
<T, K extends keyof T>(name: K, obj: T): T[K];
<K extends PropertyKey>(name: K): <T extends Record<K, unknown>>(obj: T) => T[K]
}
const prop: Prop = (name: any, obj?: any) => {
if (obj === undefined) {
return (obj: any) => obj[name]
} else {
return obj[name]
}
}
// weak types used here for simplicity like in first solution
const valid1 = prop('foo', { foo: 'hello1' }); // string
const valid2 = prop('foo')({ foo: 'hello2' }); // string
const invalid = prop('baz')({ foo: 'hello' }); // never
console.log(valid1, valid2) // hello1 hello2
Sample
Note: Function overloads and function types cannot be used completely interchangeably (more infos here or here). With latter, it may be necessary to annotate types with any
in the function implementation part to account for incompatible return types in the defined call signatures - checkout this playground for an example.
回答2:
Probably a bit verbose, but does what it needs to:
interface Prop {
<K extends PropertyKey, T extends {}>(name: K, object: T): K extends keyof T ? T[K] : undefined;
<K extends PropertyKey>(name: K): <T extends {}>(object: T) => K extends keyof T ? T[K] : undefined;
}
declare const prop: Prop;
const invalid = prop('bar')({ foo: 'hello world' });
const valid = prop('foo')({ foo: 'hello world' });
const sLen = prop('length', 'Hello World');
const arity = prop('length')((a: number, b: number, c: number) => a + b + c);
来源:https://stackoverflow.com/questions/60203078/typescript-type-safe-curried-functions