从JavaScript到TypeScript,Pt。 III:类型推断和兼容性

Considering it's called TypeScript, it's probably no surprise that there's more to types than annotating a variable as a string. The compiler does a lot of work under the hood to put those types to work, and figure out what they should be even when you leave the annotations out.

考虑到它被称为类型脚本( Type Script),类型不只是将变量注释为string ,这不足为奇。 编译器做了很多工作来使这些类型起作用,并弄清楚它们应该是什么,即使您省略了注释也是如此。

This article will explore how TypeScript determines:

本文将探讨TypeScript如何确定:

  1. The types of varibles you choose not annotate; and

    您选择的不标注类型的变量; 和
  2. Whether assignment statements are safe -- that is, if the value you're assigning "fits" into the type of the variable you're trying to put it in.

    赋值语句是否安全-也就是说,如果您要为变量类型分配“适合”的值,则尝试将其放入。

I'm assuming you're using Node to run the sample code; I'm running v6.0.0. To get the code, clone my repo, and checkout the Part_3 branch:

我假设您正在使用Node运行示例代码; 我正在运行v6.0.0。 要获取代码,请克隆我的repo ,并检出Part_3分支:

git clone http://github.com/Peleke/oop_in_typescript
git checkout Part_3-Type_Inference_and_Compatibility

You can compile all the .ts files with tsc -t ES5, which will put the compiled Javascript in a folder called built. It'll also produce an unsettling litany of errors, but you can ignore them.

您可以编译所有.ts文件与tsc -t ES5 ,这将使编译JavaScript中的文件夹,名为在built 。 它还会产生一系列令人不安的错误,但是您可以忽略它们。

类型推断 ( Type Inference )

TypeScript doesn't force you to annotate types in your code. With few exceptions, vanilla ES6 is perfectly valid. Even so, TypeScript will always try to figure out the types of unannotated variables , so you get the benefits of type-checking without the hassle of having to do it yourself.

TypeScript不会强制您注释代码中的类型。 除少数例外,香草ES6完全有效。 即便如此,TypeScript仍将始终尝试找出未注释变量的类型,因此您将获得类型检查的好处,而不必亲自进行操作。

// basic_inference.ts

"use strict";

// Requires string inputs
function buildName (first_name : string, last_name : string, title : string) : void {
    if (title)
        return `${title} ${first_name} ${last_name}`
    else
        return `${first_name} ${last_name}`
}

// No annotations . . . 
const first_name = l337,
     last_name = 'Haxor',
     title             = 'Dr';

// . . . But TypeScript's a smart language that don't need no annotations
console.log(buildName(first_name, last_name, title)); // TypeError

If you run the code snippet above, you'll get a type error, even though there's not an annotation in sight. Type Inference is the process of determining an unannotated variable's type, and this is it in action.

如果运行上面的代码段,即使看不到注释,也会出现类型错误。 类型推断是确定未注释变量的类型的过程,这就是它的作用。

Example of TypeError thrown by unannotated variable

The rule for primitive types is that TypeScript knows them when it seems them: You don't need to annotate these.

基本类型的规则是TypeScript在看起来它们时就知道它们:您无需注释它们。

"use strict";

function queryUrl( url : string ) : any {
    let result;
    // Do network magic
    return result;
}

// All TypeErrors.

let url = 42;
queryUrl(42); 

url = { url : 'http://google.com' };
queryUrl(url);

url = false;
queryUrl(url);

It's with collections, classes, and interfaces that things get hairy.

通过集合,类和接口,事情变得很繁琐。

联合类型 ( Union Types )

TypeScript uses union types in more complicated cases.

在更复杂的情况下,TypeScript使用联合类型

A ** union type ** is somewhere between a specific type and any. A ** union type gives a list of types that a variable can contain, or that a value can have **.

** 联合类型 **位于特定类型和any类型之间。 **联合类型给出了变量可以包含的类型列表,或者值可以具有**的类型。

You denote a union type as a parenthesized list of types separated by a pipe. A union type satisfied by either a string or a number looks like: (string | number).

您将联合类型表示为用管道括起来的类型的括号列表。 由字符串或数字满足的并集类型看起来像: (string | number)

// union.ts

"use strict";

class Article {

    static articles : Article[] = [];

    // Function overload
    static submit (article) : boolean;
    static submit (article) : Article;
    static submit (article  : Article) : any {
        if (Article.articles.length >= 1)
            return article;
        else {
            Article.articles.push(article)
      return true;
    }
    }

    constructor (public text : string) { }

}

// Submit returns true if the submission was successful, and the
//    article that failed to be submitted, otherwise.
const bool : (boolean | Article) = Article.submit(new Article("It was the best of times . . . ")), // true
      obj  : (boolean | Article) = Article.submit(new Article("Wasn't my best article, anyway . . . ")); // article

console.log(bool); // true
console.log(obj); // Article { text : 'Wasn\t my best article, anyway . . . ' }

A union is useful for enumerating options in situations where you expect one of a set of well-defined types to come through, but can't be sure of exactly which. Common examples of this include:

在您希望一组定义明确的类型之一通过但无法确切确定哪种类型的情况下,联合对于枚举选项很有用。 常见的示例包括:

  1. Variables receiving return values from functions that return values of different types for the same input depending on state (submit); and

    变量根据状态从不同的函数接收返回值,这些函数针对同一输入返回不同类型的值( submit ); 和
  2. Setting the type for a collection, such as an array, which may naturally contain a well-defined set of different types.

    设置集合的类型(例如数组),该集合自然可以包含一组定义明确的不同类型。

Speaking of collections . . .

说到收藏。 。 。

最佳普通类型 ( Best Common Type )

Untyped Maps and Sets automatically have the type any:

未输入类型的地图和集会自动具有any类型:

"use strict";

const map = new Map(),
    set    = new Set();

// No trouble with either of these . . .
map.set('1', 'spitting'); // String key, String value
map.set(1, 'game'); // number key, String value

// . . . Nor any of these.
set.add(2718); // number
set.add('Euler\'s Constant'); // String
set.add(true); // boolean

The algorithm for determining the right type for an array of arbitrarily typed elements is more complicated.

确定任意类型元素数组的正确类型的算法更加复杂。

推断基元数组的类型 ( Inferring Types for Arrays of Primitives )

There are two possibilities for unannotated arrays of primitives.

无注释的基元数组有两种可能。

  1. ** The array contains values of only one type**. If the array contains values of type T, the array will have type T[].

    **数组仅包含一种类型的值**。 如果数组包含类型T值,则数组将具有类型T[]
  2. ** The array contains values of multiple types **. If the array contains several types, TypeScript assigns a union type over all of them.

    **数组包含多种类型的值**。 如果数组包含多个类型,TypeScript将为所有类型分配一个联合类型。

The short version is this: If you initialize your array with . . .

简短的版本是这样的:如果您使用初始化数组。 。 。

  • No elements, it will have type any[]. You can add whatever you like to it later.

    没有元素 ,它将具有any[]类型。 您可以稍后添加任何内容。
  • Elements of one type, T, it will have type T[]. You can only add values of type T to it ater.

    一种类型 T 元素将具有类型T[] 。 您只能向其添加类型T值。
  • Elements of several primitive types -- say, A, B, and C -- you can add any value of type A, B, or C to it later. It will have the union type (A | B | C)[].

    几种原始类型的元素(例如ABC )可以在以后向其添加ABC类型A任何值。 它将具有联合类型 (A | B | C)[]
// collection_inference.ts

"use strict";

const something_and_nothing = [99, 100];

// These all work.
something_and_nothing.push(101);  
something_and_nothing.push(null);  
something_and_nothing.push(undefined);  

const names_and_numbers = ['Adonis', 'Aphrodite', 12];

// This throws, because a Boolean is neither a String nor a Number.
names_and_numbers.push(true); 

If you try this, you'll get an error, because true -- a boolean value -- doesn't belong in an array of type string | number.

如果尝试这种方法,则会出现错误,因为trueboolean值)不属于string | number类型的数组。 string | number

boolean not assignable to array for strings and numbers.

What happened here?

这里发生了什么?

  1. We declared an untyped array, called names_and_numbers, and tossed some values inside.

    我们声明了一个无类型的数组,命名为names_and_numbers ,并向其中扔了一些值。
  2. Then, TypeScript looked at every value inside of it, and created a set of the types it found: In this case, string and number.

    然后,TypeScript检查其中的每个值,并创建一组找到的类型:在这种情况下,为stringnumber
  3. Finally, TypeScript selects the type in the set that satisfies every value. In other words, it assigns the array the best common type -- ideally, the supertype of all the values.

    最后,TypeScript在集合中选择满足每个值的类型。 换句话说,它为数组分配最佳公共类型 -理想情况下,是所有值的超类型。

When TS meets the first array, it sees that every value is a number, and types the list accordingly. We can add 101 without trouble because it's a number. null and undefined are allowed, because they're subtypes of every type.

TS遇到第一个数组时,它将看到每个值都是一个number ,并相应地键入列表。 我们可以轻松添加101 ,因为它是一个数字。 允许使用nullundefined ,因为它们是每种类型的子类型。

"null is considered a valid value for all primitive types, object types, union types, intersection types, and type parameters, including even the Number and Boolean primitive types . . . [and] undefined is considered a valid value for all primitive types, object types, union types, intersection types, and type parameters." ~ TypeScript Specification, §3.2.6-7

“ null被视为对所有基本类型,对象类型,联合类型,交集类型和类型参数的有效值,甚至包括Number和Boolean基本类型... [和] undefined被视为对所有基本类型的有效值,对象类型,联合类型,交集类型和类型参数。” 〜TypeScript规范,第3.2.6-7节

TypeScript does the same for the second list. It walks through the array; creates a set of the types it finds (string, number); and chooses the supertype.

TypeScript对第二个列表执行相同的操作。 它遍历数组; 创建一组找到的类型( stringnumber ); 并选择超类型。

. . . But there is no supertype, because a number isn't a string, and a string isn't a number, In this case, TypeScript realizes that each element is either a string or a number, and assigns a union type, (string | number), instead.

。 。 。 但没有超,因为number并不是一个stringstring不是一个number ,在这种情况下,打字稿意识到每个元素可以是一个string 或者 number ,并分配一个联合类型, (string | number)

推断对象数组的类型 ( Inferring Types for Arrays of Objects )

For the most part, inferring the type of an array containing objects is a symmetrical problem. Most of the type inferences you'll deal with fall into one of two categories:

在大多数情况下,推断包含对象的数组的类型是一个对称问题。 您将要处理的大多数类型推断都属于以下两类之一:

  1. The objects ** share a nominal superclass *, but * no instance of the superclass is in the array ** . This results in a union type.

    对象**共享名义上的超类* ,但是*数组中没有超类的实例。 这导致联合类型。
  2. The objects ** share a nominal superclass *, and * an instance of the superclass is in the array **. In this case, the array will have the superclass type.

    对象**共享名义上的超类* ,并且*超类的实例位于数组**中。 在这种情况下,数组将具有超类类型。

There are unintuitive edge cases here, so the best approach is to always type your object arrays.

这里存在不直观的边缘情况,因此最好的方法是始终键入对象数组。

共同的超类案件。 。 。 或不 (The Case of the Common Superclass . . . Or Not)

Consider the two arrays at the bottom.

考虑底部的两个阵列。

// hierarchical_inference.ts

"use strict";

// This is a textbook example of a senseless class hierarchy: This should be an interface. 
//   But  you can't instantiate an interface, so I'm using a class for illustration.
class Named { name : string; }

"use strict";

class Nameable {

  constructor (public name : string) { }

}

class Person extends Nameable {

  private boolean : married;

  constructor (name : string) { super(name) }

}

class Museum extends Nameable {

  private open : boolean;

  constructor (name : string) { super(name) }

}

class Planet extends Nameable {

  private habitable : boolean;

  constructor (name : string) { super(name) }

}

    // (Museum | Planet)[]
const union   = [new Museum('MOMA'), new Planet('Zeltron')],
    // Nameable[]
       proper  = [new Person('Gauss'), new Museum('Met'), new Planet('Xaltrix'), new Nameable()];

Here, union has type (Museum | Planet)[], but Named[] is the better choice.

在这里, union具有(Museum | Planet)[] ,但是Named[]是更好的选择。

This is because, when TypeScript selects a best common type from the set of the types in a given array, * it can only choose from the types actually in the array **. Since TypeScript doesn't find an instance of Named in union, it can't figure out that it's the correct superclass. Instead, it settles for the next best thing: A union type acounting for whatever classes it *does find.

这是因为,当TypeScript从给定数组的类型集中选择最佳的通用类型时, *它只能从数组**中的实际类型中进行选择。 由于TypeScript在union找不到Named的实例,因此无法确定它是正确的超类。 取而代之的是,它满足于下一件最好的事情:一个联合类型,说明它发现的所有类

TypeScript gets it right with proper, however, since this array does contains an instance of Named. Since it's in the set of possible types that TS can choose from, TypeScript identifies that it's the superclass of all the other types in the array, and types it accordingly.

但是TypeScript proper做到了proper ,因为此数组确实包含Named的实例。 由于它位于TS可以选择的可能类型集中,因此TypeScript会确定它是数组中所有其他类型的超类,并相应地键入它。

Again, best practice is to type arrays containing class instances. TypeScript can't always make the "obvious" choice when dealing with user-defined hierarchies.

同样,最佳实践是键入包含类实例的数组。 在处理用户定义的层次结构时,TypeScript不能总是做出“显而易见的”选择。

类型兼容性 ( Type Compatibility )

Let's say I have an object, and I want to stuff it in a variable of type T. TypeScript only lets me do that if the type of the value is ** compatible ** with the type of the variable. The notion of a value being compatible with the type of its container is called ** type compatibility **.

假设我有一个对象,并且要将其填充到类型T的变量中。 仅当值的类型与变量的类型兼容时,TypeScript才允许我这样做。 值与其容器类型兼容的概念称为**类型兼容性**。

The obvious case is when the object is of the same type as the variable. If I try to shove a Person value into a variable of type Person, things work fine.

显而易见的情况是对象与变量的类型相同。 如果我尝试将Person值推入Person类型的变量中,则一切正常。

It gets more interesting when you try to assign a value to a variable of a different type. TypeScript rejects this outright with primitives -- assigning a number to a string variable is always wrong. But, it's flexible when it comes to assigning instances of a class or interface.

当您尝试将值分配给其他类型的变量时,它会变得更加有趣。 TypeScript完全拒绝使用原语,将number分配给string变量始终是错误的。 但是,在分配类或接口的实例时很灵活。

结构性打字 ( Structural Typing )

* Structural typing ** is a way to determine if types are compatible based on their *shapes, rather than their hierarchical relationships.

结构化类型**是一种根据类型*的形状而不是层次结构关系 来确定类型是否兼容的方法

In traditional OOP, otherwise identical objects from different class hierarchies can't be treated as equivalent; what matters is not their interfaces or data members, but their f** nominal class hierarchies **. As we'll see, TypeScript is more flexible than this.

在传统的OOP中,否则来自不同类层次结构的相同对象不能视为等效对象。 重要的不是它们的接口或数据成员,而是它们的f **标称类层次结构**。 我们将看到,TypeScript比这更灵活。

类兼容性 ( Class Compatibility )

An object's structure is effectively ** the list of its data members **. If two objects share the same data members, they're structurally equivalent.

对象的结构实际上是**其数据成员的列表**。 如果两个对象共享相同的数据成员,则它们在结构上是等效的。

// compatibility.ts

"use strict";

// Structurally identical classes are equivalent.
//   This seems obvious, but would not be the case in a classical OO language.
class Book {

  constructor (public title : string, 
               public length : number, // Printable pages
               public author : string) { }

}

class Article {

  constructor (public title : string,
               public length : number,
               public author : string) { }

}

// Structural identity
let x : Book;
x = new Article('From JavaScript to TypeScript III', 6, 'Peleke')

Two different classes that are structurally identical are compatible. This seems self-evident if you have a JavaScript background, but is disallowed in more traditional languages.

在结构上相同的两个不同类是兼容的。 如果您具有JavaScript背景,这似乎是不言而喻的,但是在更传统的语言中是不允许的。

There's one exception to this rule: * Objects with private and protected members can't be compatible with objects from different classes **, *even if they have the same shape.

该规则有一个例外: *具有private成员和protected成员的对象不能与来自不同类的对象兼容**,即使它们具有相同的形状。

// compatibility_private_members.ts

"use strict";

// Identical shapes and member names, but incompatible due to private members.
class User {

    constructor (private name : string, private age : number) { }

}

class Country {

    constructor (private name : string, private age : number) { }

}

let rando : User;
rando = new Country('Azerbaijan', 15); // Error

This won't compile, because name is private in both Country and User. This restriction is in place to enforce the fact that private members should be exposed only to instances of the owning class.

这不会编译,因为nameCountryUser中都是私有的。 设置此限制是为了强制执行以下事实:私有成员应仅​​暴露于拥有类的实例。

Separate declarations of private property error

Finally, be aware that static members on a class don't matter where type compatibility is concerned. TpeScripty only cares about instance-level members.

最后,请注意,类的静态成员与类型兼容性无关。 TpeScripty 关心实例级成员。

// compatibility_static_members.ts

"use strict";

// Identical shapes and member names, but incompatible due to public members.
class User {

  static users : User[];

    static addUser (user : User) : void {
      if (user)
        User.users.push(user);
    }

    constructor (public name : string, public age : number) { }

}

class Country {

  static COUNTRIES : number = 197;

  static getCountryCount () : string {
    return `There are ${197} countries.`;
  }

    constructor (public name : string, public age : number) { }

}

// These classes have completely different static properties and methods,
//   but their instances have the same shape, so they're compatible.
let rando : User;
rando = new Country('Azerbaijan', 15); // Works fine

This behaves as it does because static properties are defined on the class's constructor function itself, so instances can't access it via delegation. That means it doesn't influence their shape, and so shouldn't influence any checks for structural equivalence.

之所以如此,是因为静态属性是在类的构造函数本身上定义的,因此实例无法通过委托来访问它。 这意味着它不会影响其形状,因此也不应影响对结构等效性的任何检查。

接口兼容性 ( Interface Compatibility )

Compatibility works much the same for interfaces, without the caveats regarding static members and private/protected members.

兼容性对接口的工作原理几乎相同,没有对静态成员和private / protected成员的警告。

// compatibility.ts

"use strict";

interface Printable {
  print () : void;
}

class Book {

  constructor (public title : string, 
               public length : number, // Printable pages
               public author : string,
               public text : string) { }

  print () : void {
    console.log(this.text);
  }

}

// Compatibile, because Book has all of the members that Printable does.
let printable : Printable
printable = new Book('Eugene Onegin', 132, 'Alexander Pushkin', 'Not planning fun . . . ');

// We can also cast between compatible types without error.
const book : Book = new Book('1984', 222, 'Orwell', 'Perhaps one did not want to be love so much as understood.'),
  new_printable = <Printable> book;

// This doesn't work with incompatible types!
//  If you run this line, you'll get an error, because the object doesn't
//  contain a member called print.
// ==
// const broken = <Printable> { not_printable : 'Well, this was a mistake.' }

The sole criterion for Book to be structurally compatible with Printable is that it implement a void method called print. Since it does, they're compatible. It doesn't matter that Book has more members besides. This is true of class instances, as well.

BookPrintable在结构上兼容的唯一标准是,它实现了称为print的void方法。 既然如此,它们是兼容的。 Book有更多成员也没关系。 类实例也是如此。

比较功能 ( Comparing Functions )

Since we can pass functions around like anything else, TypeScript has rules for the legality of assigning function values. They check two things:

由于我们可以像其他任何东西一样传递函数,因此TypeScript具有分配函数值合法性的规则。 他们检查两件事:

  • ** Parameter lists **; and

    **参数列表**; 和
  • ** Return types **.

    **返回类型**。

参数兼容性 (Parameter Compatibility)

You can assign a function to a type if the type takes the same types of arguments, in the same order, even if the type you're assigning to expects more parameters.

如果类型接受相同类型的参数,并且顺序相同,则可以为该类型分配功能,即使您要分配给该类型的类型需要更多参数也是如此。

// function_compatibility.ts

"use strict";

let buildName = function buildName ( first_name : string, last_name : string) : string {
    return `${first_name} ${last_name}`;
}

let fetchData = function fetchData ( url : string ) : string {
    return `Fake dat from ${url}`;
}

let build = buildName, // (( first_name : string, last_name : string) => string)
    fetch = fetchData; // (( url : string) => string)

// Compatible, because fetchData's parameter list is a subset of buildName's.
build = fetchData; // LINE A

// No-go, because buildName requires a second string argument, which fetch_data doesn't support.
// fetch = buildName;

In Line A, fetchData -- the function whose value we're assigning -- is in what's called the source position. build is in what's called the target position.

在A行中, fetchData (我们要为其赋值的函数)位于所谓的位置。 build在所谓的target位置。

Throw awaying arguments like that might feel strange, but we actually do it all of the time.

像这样扔掉一些争论可能会感到奇怪,但是实际上我们一直都在这样做。

"use strict";

const users = [
    { name : 'Horus', symbol : 'wedjat eye' },
    { name : 'Set', symbol: 'was-sceptre' },
    { name : 'Neuth', symbol : 'stars' }
]

// JS passes map the current index and the whole array
//   on each iteration, but we generally throw both away.
const names = users.map((user) => user.name };

If we call a function with more arguments than it expects, it ignores everything but what it expected. This is why we can assign a function (let's call it fun) that takes fewer arguments to a value typed to take more. If we pass extra arguments, fun just ignore them.

如果我们调用的函数的参数超出预期,则它将忽略除预期之外的所有内容。 这就是为什么我们可以将一个需要较少参数的函数赋给一个要输入更多值的函数(我们称它为fun )。 如果我们传递额外的参数,那么fun就忽略它们。

TypeScript won't let us go the other way, though, because a function expecting two arguments presumably can't do its job without both of them.

但是,TypeScript不会让我们走另一条路,因为一个期望两个参数的函数可能无法在没有两个参数的情况下完成其工作。

This is actually directly analogous to sub- and superclass relationships with objects. If a function of type f can "hold" a more specific function of type g, f is a supertype of g. It's like saying that an instance of the User base class can contain an instance of a more specific child class, like LoggedInUser. . . But functions are cooler and more abstract.

实际上,这直接类似于与对象的子类和超类关系。 如果类型f的函数可以“持有”类型g的更特定的函数,则fg的超类型。 这就像说User基类的实例可以包含更具体的子类的实例,例如LoggedInUser 。 。 。 但是函数更酷,更抽象。

Just two notes to wrap up:

只需总结两个注意事项:

  1. Rest parameters don't influence this check. If two functions have compatible parameter lists, and one of them has rest params, they're still considered compatible.

    其余参数不会影响此检查。 如果两个函数具有兼容的参数列表,并且其中一个具有rest参数,则仍将它们视为兼容。
  2. Two functions can only be compatible if the return type of the source function is a subtype of the target function's return type.

    仅当源函数的返回类型是目标函数的返回类型的子类型时,两个函数才能兼容。

函数数组 (Function Arrays)

I'll hazard a guess that you probably won't create a ton of function arrays in your lifetime. I've come across it when working with Rx.js, though, so it's worth mentioning.

我可能会猜测您一生中可能不会创建大量的函数数组。 不过,在使用Rx.js时 ,我已经碰到了它,因此值得一提。

There are two common cases -- arrays of:

有两种常见情况-数组:

  1. ** Functions with completely different signatures **. In this case, TypeScript assigns the array a union type covering all of the function types.

    **具有完全不同的签名的功能**。 在这种情况下,TypeScript为数组分配一个涵盖所有函数类型的联合类型。
  2. ** Functions with compatible signatures **. In this case, TypeScript assigns the array the most general type compatible with the others.

    **具有兼容签名的功能**。 在这种情况下,TypeScript为数组分配与其他数组兼容的最通用类型。

These two can occur together, as well, which we'll see in the example.

这两个也可以同时出现,我们将在示例中看到。

函数类型的并集 (Unions Over Function Types)

If the functions in your array have completely different parameter lists and return types, TypeScript assigns the array a union type covering all of them.

如果数组中的函数具有完全不同的参数列表和返回类型,则TypeScript会为数组分配覆盖所有它们的联合类型。

// function_array_union.ts

"use strict";

const function_array = [
  // ((bar : string) => void)
  function foo (bar : string) : void { console.log(bar) },
  // ((answer : number) => string)
  function baz (answer : number) : string { 
      return `The answer to life is ${answer}.`;
   }
];

The types of these functions don't intersect at all, so TypeScript assigns the array the union type ((bar : string) => void) | ((answer : number) => string).

这些函数的类型根本不相交,因此TypeScript为数组分配联合类型((bar : string) => void) | ((answer : number) => string) ((bar : string) => void) | ((answer : number) => string)

具有兼容功能的阵列 (Arrays with Compatible Functions)

If one of your functions is compatible with the others, TypeScript will assign the array the most general function type.

如果您的函数之一与其他函数兼容,则TypeScript将为数组分配最通用的函数类型。

// function_array_compatible.ts

"use strict";

class User {

  constructor (public username : string, public email : string) { }

}

class LoggedInUser extends User {

  public logged_in : boolean = true;

  constructor (username : string, email : string) { super(username, email) }

  logout () : void {
    // Logout logic
  }

}

const function_array = [
  // ((user : LoggedInUser) => void)
  function logout(user : LoggedInUser) : void { user.logout() },
  // ((user : User, email : string) => void)
  function setEmail (user : User, email : string) : void { 
    user.email = email;
  }

];

In this case, function_array has the type ((user : User => void, email : string => void) => void)[]. This is essentially because setEmail's type is compatible with logout, but not vice-versa.

在这种情况下, function_array具有类型((user : User => void, email : string => void) => void)[] 。 这主要是因为setEmail的类型与logout兼容,反之亦然。

Finally, keep in mind that both of these things can happen at once. If one function in the array is a supertype of some others, but there are black sheeps that just don't fit in, TypeScript will create a union type of the supertype and stragglers.

最后,请记住,这两种情况都可以同时发生。 如果数组中的一个函数是其他某些函数的超类型,但有些不适合使用,则TypeScript会创建超类型和散列器的并集类型。

// function_array_compatible.ts, bottom

// Dummy class to pleae compiler
class Friend { }

const function_array2 = [
  // ((user : LoggedInUser) => void)
  function logout(user : LoggedInUser) : void { user.logout() },
  // ((user : User, email : string) => void)
  function setEmail (user : User, email : string) : void { 
    user.email = email;
  },
  // ((user : User, amount : number) => void)
  function chargeAccount ( user : User, amount : number) : void {
    // Take user's money to fund more features
  }
];

In this case, function_array2 will have the gnarly type (((user: User, email: string) => void) | ((user: User, amount: number) => void))[]. This is just a union type covering the type we had in the last section, along with the type of chargeUser.

在这种情况下, function_array2将具有粗糙类型(((user: User, email: string) => void) | ((user: User, amount: number) => void))[] 。 这只是一个联合类型,涵盖了上一节中的类型以及chargeUser的类型。

Again, these rules are no different from those in place when dealing with arrays of objects. All we've done is generalize the notion of supertype according to TypeScript's rules of function compatibility.

同样,这些规则与处理对象数组时的规则没有什么不同。 我们所做的只是根据TypeScript的功能兼容性规则来概括超类型的概念。

Don't sweat it if this is a bit alien, though. I've had to know these rules before, but it only comes up every once in awhile.

但是,如果这有点陌生,请不要流汗。 我以前必须知道这些规则,但是它每隔一段时间才会出现一次。

结论 ( Conclusion )

There's more to type compatibility, and the spec contains the exhaustive details.

类型兼容性还有更多, 规范包含了详尽的细节

I find that details beyond these are mostly of theoretical interest, though: This is the 80/20 of what I've needed to understand when debugging sloppily typed code or dealing with generics, which we'll take a close look at next time.

我发现,除此以外的其他细节主要是理论上的关注:这是调试松散类型的代码或处理泛型时我需要了解的80/20,我们将在下一次仔细研究。

As always, feel free to leave questions in the comments, or shoot them to me on Twitter (@PelekeS)!

与往常一样,随时在评论中留下问题,或在Twitter( @PelekeS )上向我射击!

翻译自: https://scotch.io/tutorials/from-javascript-to-typescript-pt-iii-type-inference-compatibility

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值