《鸭子》打字与打字稿中的函数参数 [英] "Duck" Typing vs. Function Arguments in Typescript

查看:26
本文介绍了《鸭子》打字与打字稿中的函数参数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Typescript 执行鸭子"在某些情况下键入,例如当您针对接口检查函数参数的有效性时.

例如:

接口命名为{名称:字符串}函数打印名称(x:命名){返回 x.name;}const myVar = {姓名:约翰",happy: "OK",//这个额外的键值对不会破坏我们的 printName 函数};打印名称(我的变量);

然而,当你创建一个变量并定义它的类型时,一个额外的键值对抛出一个类型错误:

const myVar: Named = { name: "Jim", extraVal: "Oops";}//extraVal";不被允许.

1) 为什么 Typescript 在第二个实例中检查 exact 匹配,而不检查传递给函数的参数?

2) 是否还有其他使用鸭子类型的实例,如何区分这些实例?

解决方案

TypeScript 的类型系统是 structural(您称之为鸭子"类型),并且一般来说,额外的属性不被视为违反对象类型的结构.换句话说,TypeScript 中的对象类型是开放/可扩展的".而不是关闭/精确";已知类型 {a: string} 具有字符串值 a 属性,但不知道缺乏其他属性.

开放对象类型启用诸如interfaceclass 扩展等有用的东西,所以如果Y extends X 那么你可以使用Y 任何可以使用 X 的地方,即使 Y 具有更多功能.

因此,要回答您的第二个问题,该语言中的大多数地方仅依赖于结构子类型.


据我所知,编译器表现为对象类型准确的唯一地方是当您创建新的对象字面量时.编译器假定当您创建一个对象字面量时,您会关心它的所有属性.如果您立即将这样的文字分配给不知道所有对象文字属性的类型,编译器会警告您:编译器将忘记这些额外的属性并且无法跟踪它们,这可能是您的错误部分.这称为过度属性检查.只有当你有一个新鲜"的食物时它才会开始.对象字面量(尚未在任何地方分配)并将其分配给不期望其所有属性的类型.

手册中给出的为什么需要进行此检查的示例涉及拼写错误的可选属性.如果你有一个像 {weird?: boolean } 类型的变量,并将对象字面值 { wierd: true } 分配给它,编译器会说嗯,这个值确实适合该类型.它没有 weird 属性,这很好,因为它是可选的.但是它有这个额外的 wierd 属性,我会立即忘记它;为什么会有人这样做?也许这是一个错误."我不知道你是否同意这个推理,但就是这样.


所以回答你的第一个问题,编译器很满意

const myVar = {姓名:约翰",开心:好"};打印名称(我的变量);

因为对象字面量在其初始赋值中没有加宽(myVar 的类型已知具有 namehappy属性),当您将它传递到 printName() 时,它不再是新鲜的".编译器不会知道 printName() 实现中的 happy 属性,但它知道 中的 happy 属性myVar.

而且很不满意

const myVar: Named = { name: Jim", happy: OK";};

因为它被过度的属性检查所捕获.myVar 的类型将不包含对 happy 的任何引用.


好的,希望有帮助;祝你好运!

Typescript performs "duck" typing in certain circumstances, such as when you are checking the validity of an argument to a function against an interface.

For example:

interface Named {
  name: string
}

function printName(x: Named) {
  return x.name;
}

const myVar = {
  name: "John",
  happy: "OK", // This extra key-value pair does not break our printName function
};

printName(myVar);

However, when you create a variable and define its type, an extra key-value pair will throw a type error:

const myVar: Named = { name: "Jim", extraVal: "Oops" } // The "extraVal" is not allowed.

1) Why does Typescript check for the exact match in the second instance, but not with the parameter passed to the function?

2) Are there other instances when duck-typing is used, and how can one tell these instances apart?

解决方案

TypeScript's type system is structural (which you're calling "duck" typing), and in general, extra properties are not considered to violate the structure of an object type. In other words, object types in TypeScript are "open/extendible" and not "closed/exact"; the type {a: string} is known to have a string-valued a property, but is not known to lack other properties.

Open object types enable useful things like interface and class extension, so if Y extends X then you can use a Y everywhere you could use an X, even if Y has more functionality.

So to answer your second question, most places in the language rely only on structural subtyping.


As far as I know, the only place where the compiler acts as if object types were exact is when you create a new object literal. The compiler assumes that when you create an object literal that you care about all its properties. If you then immediately assign such a literal to a type that does not know about all the object literal's properties, the compiler warns you: the compiler will forget about these extra properties and be unable to track them, and it might be a mistake on your part. This is called excess property checking. It only kicks in when you have a "fresh" object literal (that has not yet been assigned anywhere) and you assign it to a type that does not expect all its properties.

The example given in the handbook for why this check is desirable involves misspelling optional properties. If you have a variable of a type like { weird?: boolean } and assign to it the object literal { wierd: true }, the compiler says "hmm, this value does fit the type. It has no weird property, which is fine because it's optional. But it has this extra wierd property that I'm going to immediately forget about; why would someone do that? Maybe that's an error." I don't know whether you agree with this reasoning or not, but there it is.


So to answer your first question, the compiler is happy with

const myVar = {
  name: "John",
  happy: "OK"
};    
printName(myVar);

because the object literal is not widened in its initial assignment (the type of myVar is known to have both a name and a happy property), and by the time you pass it into printName(), it's no longer "fresh". The compiler will not know about the happy property inside the implementation of printName(), but it does know about the happy property in myVar.

And it's unhappy with

const myVar: Named = { name: "Jim", happy: "OK" };

because it gets caught by excess property checking. The type of myVar will not contain any reference to happy.


Okay, hope that helps; good luck!

这篇关于《鸭子》打字与打字稿中的函数参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆