【问题标题】:TypeScript error 2345 while redefining Object Property method重新定义对象属性方法时出现 TypeScript 错误 2345
【发布时间】:2021-12-15 00:48:31
【问题描述】:

这是一个衍生问题

Object Property method and have the good inference of the function in TypeScript

谢天谢地,@jcalz 的代码有效;

const P = <T,>(x: T) => ({ "foo": <U,>(R: (x: T) => U) => R(x) });

现在我编写了一个高级代码,以避免每次函数调用都创建新对象。

想法是首先用_P(undefined)创建一个基础对象,然后通过Object.defineProperty改变属性值来重用该对象。

它几乎可以工作,但如下所示,会产生类型错误:2345:

'(a: number) => number' 类型的参数不能分配给'(x: undefined) => number' 类型的参数。 参数“a”和“x”的类型不兼容。 类型“未定义”不可分配给类型“数字”。(2345)

const f: (a: number) => number

const _P = <T,>(x: T) =>
  ({ "foo": <U,>(R: (x: T) => U) => R(x) });

const P =
  (obj =>
    <T,>(x: T) =>
      Object.defineProperty(obj, "foo", {
        configurable: true, //property value changes
        value: <U,>(R: (x: T) => U) => R(x)
      })
  )(_P(undefined));

const f = (a: number) => a + 1;

const b = P(99)['foo'](f); //const b: number  //good
//however, `f` is marked as type error 2345

console.log(b);//100

TypeScript playground link

有解决此错误的想法吗?谢谢!

【问题讨论】:

    标签: typescript


    【解决方案1】:

    这是因为当您调用Object.defineProperty 时,打字稿无法推断该字段的类型已更改。实际上你不应该在这里使用defineProperty,它没有任何意义,只需使用正常的赋值即可。所以让我们稍微简化一下重写这段代码:

    // Not quite clear to me why you wrapped this in an immediately
    // invoked function, but for simplicity I'll unwrap it
    const obj = _P(undefined)
    const P =
      <T>(x: T) => {
        // Don't use `defineProperty` because there is no point in it
        // Also name the argument for `R` function differently
        // in order to not get confused what `x` actually is
        obj.foo = <U>(R: (arg: T) => U) => R(x)
      };
    

    这里defineProperty居然隐藏了真正的错误:

    Type '<U>(R: (arg: T) => U) => U' is not assignable to 
      type '<U>(R: (arg: undefined) => U) => U'
    

    defineProperty 只是绕过了类型检查,因此您看不到导致错误的原因。

    谈到修复它,我真的无法提出任何建议,因为我不清楚您要做什么。当P 在每次调用后创建一个新对象时,很明显:多个调用者可以获取自己的该对象实例和foo 函数,每个实例都可以接受自己的参数类型。然而,情况并非如此:整个应用程序只有一个{foo: ...} 对象,因此它不可能接受不同类型的参数。请记住,在P 调用之后立即调用foo 函数不是强制性的,例如想象一下这种情况。

    const b = P('abc')
    const c = P(99)
    

    它应该如何工作?请记住,整个应用程序中只有一个{ foo: ... } 对象。在上面的sn-p中实例化b之后,这个对象就变成了

    // (1)
    { 
      foo: <U>(R: (arg: string) => U) => R('abc'),
    }
    

    但是在实例化c 之后,这个相同的对象发生了变化:它突然变成了

    // (2)
    { 
      foo: <U>(R: (arg: number) => U) => R(9),
    }
    

    例如,当您致电b.foo(a =&gt; a.repeat(2)) 时,现在应该发生什么?根据您的预期,您传递给foo 的函数应该用'abc' 调用,它应该返回'abcabc'。然而 foo 函数现在就像在 sn-p (2) 中一样,所以实际发生的是你传递给 foo 的函数将被 99 调用,它没有一个名为 repeat 的方法所以这将引发运行时错误。所以无论你想做什么,这都行不通。

    此外,“避免每次调用都创建新对象”的动机还不是很清楚。在 javascript 中,创建对象所需的时间和它占用的内存量通常可以忽略不计。你绝对不应该关心它,除非你正在编写一些可笑的性能密集型的东西,或者除非你有数亿个这样的对象。要考虑的另一件事是,改变对象并不总是一件好事,尤其是在这种情况下。这很难管理,会引起很多头痛。因此,请在实施您尝试实施的任何内容之前三思而后行

    【讨论】:

    猜你喜欢
    • 2019-07-23
    • 1970-01-01
    • 2020-02-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-14
    • 1970-01-01
    相关资源
    最近更新 更多