【问题标题】:Simple typescript example of "supplied parameters do not match call signature of target"“提供的参数与目标的调用签名不匹配”的简单打字稿示例
【发布时间】:2017-12-02 13:30:18
【问题描述】:

我正在学习 Typescript,并且一直在研究这个例子:

interface Thing {
    a: number;
    b: boolean;
    c: string;
}

let obj = <Thing>{
    a: 5,
    b: true,
    c: 'string that says string'
}

function x(someObj: Thing): string {
    return someObj.c;
}

function func(someObj: Thing, x: () => string) {
    return x(someObj);
}

console.log(func(obj, x))

我在 func 函数的 return 语句中的 x(someObj) 和最后一行对 func 的调用中的 x 都得到相同的错误。

这是错误:

提供的参数与目标的调用签名不匹配

但是,如果我将编译后的版本粘贴到控制台中,它会通过记录“表示字符串的字符串”来工作。

var obj = {
    a: 5,
    b: true,
    c: 'string that says string'
};
function x(someObj) {
    return someObj.c;
}
function func(someObj, x) {
    return x(someObj);
}
console.log(func(obj, x));     //string that says string

我在 Typescript Playground 中使用编译器:

https://www.typescriptlang.org/play/index.html

我已经查看了有关 stackoverflow 的其他问题和答案,但它们似乎与更复杂的 Angular 问题有关,我不明白。

【问题讨论】:

  • 不应该是:x: (someObj: Thing) =&gt; string吗?您必须匹配x 的签名,该签名采用接口Thing 的一个参数。
  • 哦,是的,这行得通。多谢了。我想我想既然我已经在它自己的定义中声明了 x 的类型,那么我就完成了。

标签: javascript typescript


【解决方案1】:

为了扩展您的评论,对于 Andrew Li 的(应该是什么)正确答案,您实际上已将自己锁定在您创建的角落,通过 OVER-TYPING。

通过显式键入所有内容,您可能看起来格外安全,但实际上您提供了额外的空间来允许不一致。

函数看起来像:

function func (obj: Thing, x): string {
  return x(obj);
}

根据您的版本和设置,它可能工作得很好(或抱怨“NO IMPLICIT ANY”)。

您所做的是为它提供一个不匹配的类型,因为您只是想提供一个一次性的东西来安抚系统。

我的意思不是要听起来有对抗性,或任何东西;我们都为此感到内疚。但是需要安抚类型系统让我们一直马虎。

我认为比较不那么痛苦的方式是这样的:

interface Transform<A, B> {
  (x:A):B;
}

interface Thing {
  a: number;
  b: boolean;
  c: string;
}

type ThingC = Transform<Thing, string>;

const x = (obj: Thing) => obj.c;
const func = (obj: Thing, x: ThingC) => x(obj);


const c = func({ a: +!0, b: !0, c: "I work fine." }, x);

如果您要在 VSCode 中加载它,我相信您会对从中获得的类型信息感到惊喜。

类型确实有利于方法签名。
当然,如果您想围绕它们使用工具,请随意将类型信息添加到 const 中:

const obj: Thing = { a: 1, b: true, c: "Yes" };

但这并不是最有益的地方;特别是因为即使obj有不同的类型,比如OtherThing,如果它也符合Thing的条件,它仍然可以进入xfunc,即使它与它无关,并且对此一无所知。

为了更一般的情况:

interface Transform<A, B> {
  (x:A):B;
}

interface Apply<A, B> {
  (x:A, f: Transform<A, B>):B;
}

interface Thing {
  a: number;
  b: boolean;
  c: string;
}

const x: Transform<Thing, string> = obj => obj.c;
const f: Apply<Thing, string> = (obj, x) => x(obj);

const c = f({ a: 1, b: true, c: "" }, x);

如果你犯了任何类型错误,它就会对你大喊大叫,而且你仍然使用仍在严格类型检查的文字调用函数。

想要一些有趣的东西吗?

const g = <A, B>(o: A, x: Transform<A, B>):B => x(o);
const d = g({ a: 1, b: true, c: "" }, x);

你没有告诉gANYTHING它正在处理什么类型。这是一个匿名函数,具有匿名类型,它被传递了一个转换。

仍然知道返回到d的类型,并且它仍然知道oThing(不管它是什么类或者它有什么接口)。它知道这一点,因为它从 x 中提取了这些类型并向后工作。

所以现在你有:

interface Transform<A, B> { (x:A):B; }
interface Thing { a: number; b: boolean; c: string; }

const x = (obj: Thing) =>
  obj.c;

const g = <A, B>(o: A, x: Transform<A, B>):B =>
  x(o);

const d = g({ a: 1, b: true, c: "" }, x);

仍然得到d正确。
使用这样的类型对您来说似乎违反直觉,但实际上,您可以通过依靠类型推断的优势,而不是依靠手动使类型系统对额外的噪音感到满意,从而在正确性方面为自己带来很大的好处这可能与它认为应该有的相冲突。

【讨论】:

  • 哇,这是一个很好的答案 Norguard。感谢您投入大量精力。
  • 我已将其标记为正确,但由于我缺乏声誉点,我的赞成票不可见。另外,我非常感谢您如何使用箭头函数完成此操作。而且,是的,这真的很好用!
猜你喜欢
  • 2016-06-09
  • 2017-04-28
  • 2017-08-24
  • 2017-02-25
  • 2017-07-26
  • 2018-03-31
  • 2017-05-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多