Typescript / Type Safe Curried Functions

六眼飞鱼酱① 提交于 2021-02-07 20:48:29

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!