【问题标题】:Typescript / Type Safe Curried Functions打字稿/类型安全的咖喱函数
【发布时间】:2020-05-28 21:08:26
【问题描述】:

如何在打字稿中安全地键入柯里化函数? 特别是下面的例子

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

【问题讨论】:

标签: javascript typescript typescript-typings type-inference


【解决方案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

注意: 函数重载和函数类型不能完全互换使用(更多信息herehere)。对于后者,可能需要在函数实现部分使用 any 注释类型,以解决定义的调用签名中不兼容的返回类型 - 以 playground 为例。

【讨论】:

  • 我不会真的说函数重载是proper way,因为我不确定是否必须使用function 关键字或只是多次声明一个函数。你能指出这是在哪里说的吗?看起来这两种方式都是完全等价的。您的第一个示例给出了编译错误,因为它不接受任何非Record&lt;K。您的第二个声明 objundefined 看起来有点错误 imo。
  • 我同意你的观点,这种说法过于尖锐(会改变)。动机是,如果调用签名的返回类型不兼容,函数实现部分可能会在第二种情况下产生编译错误。所以这里需要用显式的any 来弱化类型(反对函数重载)。在您的特定情况下,该限制并不明显。 PS:第一个示例中的编译错误是故意的 - 您应该无法选择对象中不存在的属性。
  • 再次尖锐,“你应该不能选择一个属性,它不存在于对象中。” ......我正是需要那个。看看R.prop_.get
  • 嗯?您的invalid 示例表明相反​​。如果你真的想访问一个可能不存在的道具,你应该返回 undefined 而不是 never 并且可能通过命名方法 maybeProp/propOrUndefined 或允许设置默认值来暗示这种行为。在这种情况下,您可以省略约束。通常,类型语言需要快速失败的编译错误。
【解决方案2】:

可能有点冗长, 但做了它需要做的事情:

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);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-18
    • 2019-04-14
    • 2016-08-26
    • 2012-10-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多