TypeScript语言规范

  • 3 类型
    • 3.1 Any类型
    • 3.2 基本类型
      • 3.2.1 Number类型
      • 3.2.2 Boolean类型
      • 3.2.3 String类型
      • 3.2.4 Symbol类型
      • 3.2.5 Void类型
      • 3.2.6 Null类型
      • 3.2.7 Undefined类型
      • 3.2.8 Enum类型
      • 3.2.9 String类型
    • 3.3 Object类型
      • 3.3.1 命名类型引用
      • 3.3.2 Array类型
      • 3.3.3 Tuple类型
      • 3.3.4 Function类型
      • 3.3.5 Constructor类型
      • 3.3.6 Members
    • 3.4 Union类型
    • 3.5 Intersection类型
    • 3.6 Type参数
      • 3.6.1 Type Parameter列表
      • 3.6.2 Type Argument列表
      • 3.6.3 This类型
    • 3.7 命名类型
    • 3.8 指定类型
      • 3.8.1 预定义类型
      • 3.8.2 类型引用
      • 3.8.3 Object类型文法
      • 3.8.4 Array类型文法
      • 3.8.5 Tuple类型文法
      • 3.8.6 Union类型文法
      • 3.8.7 Intersection类型文法
      • 3.8.8 Function类型文法
      • 3.8.9 Constructor类型文法
      • 3.8.10 类型查询
      • 3.8.11 This类型推导
    • 3.9 指定成员
      • 3.9.1 属性签名
      • 3.9.2 Call签名
        • 3.9.2.1 Type参数
        • 3.9.2.2 参数列表
        • 3.9.2.3 Return类型
        • 3.9.2.4 特定签名
      • 3.9.3 Construct签名
      • 3.9.4 索引签名
      • 3.9.5 方法签名
    • 3.10 类型别名
    • 3.11 Type关系
      • 3.11.1 明显成员
      • 3.11.2 Type和Member类型
      • 3.11.3 子类型和超类型
      • 3.11.4 赋值兼容性
      • 3.11.5 额外属性
      • 3.11.6 上下文签名实例化
      • 3.11.7 类型推导
      • 3.11.8 递归类型
    • 3.12 加宽类型

3 类型

TypeScript将可选的静态类型添加到JavaScript。类型用于对程序实体(例如函数,变量和属性)施加静态约束,以便编译器和开发工具可以在软件开发期间提供更好的验证和帮助。 TypeScript的静态编译时类型系统紧密地模拟了JavaScript的动态运行时类型系统,从而使程序员可以准确地表达其程序运行时预期存在的类型关系,并通过TypeScript编译器对这些假设进行了预先验证。 TypeScript的类型分析完全在编译时进行,并且不增加程序执行的运行时开销。

TypeScript中的所有类型都是单个顶部类型的子类型,称为Any类型。 any关键字引用此类型。 Any类型是可以无限制地表示任何JavaScript值的一种类型。所有其他类型都分类为基本类型,对象类型,联合类型,交集类型或类型参数。这些类型在其值上引入了各种静态约束。

基本类型是Number,Boolean,String,Symbol,Void,Null和Undefined类型以及用户定义的枚举类型。 number,boolean,string,symbol和void关键字分别引用Number,Boolean,String,Symbol和Void基本类型。 Void类型的存在纯粹是为了指示不存在值,例如在没有返回值的函数中。不可能显式引用Null和Undefined类型-使用null和undefined文字只能引用这些类型的值。

对象类型是所有类,接口,数组,元组,函数和构造函数类型。类和接口类型是通过类和接口声明引入的,并通过在其声明中为其指定的名称进行引用。类和接口类型可以是具有一个或多个类型参数的通用类型。

联合类型表示具有多种类型之一的值,交集类型表示同时具有多种类型的值。

类,属性,函数,变量和其他语言实体的声明将类型与这些实体相关联。形成类型并将其与语言实体关联的机制取决于特定类型的实体。例如,命名空间声明将命名空间与匿名类型相关联,该匿名类型包含与该命名空间中导出的变量和函数相对应的一组属性,而函数声明则将该函数与匿名类型相关联,该匿名类型包含与参数相对应的调用签名并返回函数的类型。类型可以通过显式类型注释与变量关联,例如

var x: number;

或通过隐式类型推断,如

var x = 1;

推断“ x”的类型为Number原语类型,因为这是用于初始化“ x”的值的类型。

3.1 Any类型

Any类型用于表示任何JavaScript值。 Any类型的值支持与JavaScript中的值相同的操作,并且对Any值的操作执行最少的静态类型检查。 具体来说,可以通过Any值访问任何名称的属性,并且Any值可以用作带有任何参数列表的函数或构造函数。

any关键字引用Any类型。 通常,在没有显式提供类型并且TypeScript无法推断一个类型的地方,将假定为Any类型。

Any类型是所有类型的超类型,并且可以分配给所有类型或从所有类型分配。

一些例子:

var x: any;             // Explicitly typed
var y;                  // Same as y: any
var z: { a; b; };       // Same as z: { a: any; b: any; }function f(x) {         // Same as f(x: any): void  console.log(x);
}

3.2 基本类型

基本类型为Number,Boolean,String,Symbol,Void,Null和Undefined类型以及所有用户定义的枚举类型。

3.2.1 Number类型

Number原语类型对应于类似命名的JavaScript原语类型,并表示双精度64位格式IEEE 754浮点值。

number关键字引用Number原语类型,并且数字文字可用于写入Number原语类型的值。

为了确定类型关系(第第3.11节)和访问属性(第4.13节),Number原语类型表现为具有与全局接口类型“ Number”相同属性的对象类型。

一些例子:

var x: number;          // Explicitly typed
var y = 0;              // Same as y: number = 0
var z = 123.456;        // Same as z: number = 123.456
var s = z.toFixed(2);   // Property of Number interface

3.2.2 Boolean类型

布尔基元类型对应于类似命名的JavaScript基元类型,并表示逻辑值true或false。

boolean关键字引用Boolean基本类型,true和false文字引用两个Boolean真值。

为了确定类型关系(第3.11节)和访问属性(第4.13节),布尔基元类型的行为与对象类型具有与全局接口类型“布尔”相同的属性。

一些例子:

var b: boolean;         // Explicitly typed
var yes = true;         // Same as yes: boolean = true
var no = false;         // Same as no: boolean = false

3.2.3 String类型

String原语类型对应于类似命名的JavaScript原语类型,并表示存储为Unicode UTF-16代码单元的字符序列。

string关键字引用String原语类型,并且字符串文字可用于写入String原语类型的值。

为了确定类型关系(第3.11节)和访问属性(第4.13节),String基本类型的行为与具有与全局接口类型’String’相同的属性的对象类型相同。

一些例子:

var s: string;          // Explicitly typed
var empty = "";         // Same as empty: string = ""
var abc = 'abc';        // Same as abc: string = "abc"
var c = abc.charAt(2);  // Property of String interface

3.2.4 Symbol类型

Symbol原语类型对应于类似命名的JavaScript原语类型,并表示可以用作对象属性键的唯一标记。

symbol关键字引用Symbol基本类型。 使用具有许多方法和属性的全局对象“ Symbol”可以获得符号值,并且可以将其作为函数调用。 特别是,全局对象’Symbol’定义了许多众所周知的符号(2.2.3),它们可以以类似于标识符的方式使用。 请注意,“符号”对象仅在ECMAScript 2015环境中可用。

为了确定类型关系(第3.11节)和访问属性(第4.13节),Symbol原语类型表现为具有与全局接口类型’Symbol’相同属性的对象类型。

一些例子:

var secretKey = Symbol();
var obj = {};
obj[secretKey] = "secret message";  // Use symbol as property key
obj[Symbol.toStringTag] = "test";   // Use of well-known symbol

3.2.5 Void类型

由void关键字引用的Void类型表示不存在值,并用作没有返回值的函数的返回类型。

Void类型的唯一可能值为null和undefined。 Void类型是Any类型的子类型,是Null和Undefined类型的超类型,但是Void与其他所有类型都不相关。

注意:我们可能会考虑禁止声明Void类型的变量,因为它们没有用处。 但是,由于允许将Void作为泛型类型或函数的类型参数,因此不允许Void属性或参数是不可行的。

3.2.6 Null类型

Null类型对应于类似命名的JavaScript原语类型,并且是null文字的类型。

null文字引用Null类型的唯一值。 无法直接引用Null类型本身。

Null类型是除Undefined类型之外的所有类型的子类型。 这意味着对于所有基本类型,对象类型,联合类型,交集类型和类型参数(包括Number和Boolean基本类型),null均被视为有效值。

一些例子:

var n: number = null;   // Primitives can be null
var x = null;           // Same as x: any = null
var e: Null;            // Error, can't reference Null type

3.2.7 Undefined类型

Undefined类型对应于类似命名的JavaScript原语类型,并且是undefined文字的类型。

未定义文字表示给所有未初始化变量的值,并且是Undefined类型的唯一值。 无法直接引用未定义类型本身。

未定义类型是所有类型的子类型。 这意味着对于所有基本类型,对象类型,联合类型,交集类型和类型参数,未定义均被视为有效值。

一些例子:

var n: number;          // Same as n: number = undefined
var x = undefined;      // Same as x: any = undefined
var e: Undefined;       // Error, can't reference Undefined type

3.2.8 Enum类型

枚举类型是Number原语类型的不同用户定义的子类型。 枚举类型使用枚举声明(第9.1节)声明,并使用类型引用(第3.8.2节)进行引用。

枚举类型可分配给Number原语类型,反之亦然,但不同的枚举类型不能相互分配。

3.2.9 String类型

专用签名(第3.9.2.4节)允许将字符串文字用作参数类型注释中的类型。 字符串文字类型仅在该上下文中被允许,在其他地方则不允许。

所有字符串文字类型都是String基本类型的子类型。

TODO:扩展对字符串文字类型的支持。

3.3 Object类型

对象类型由属性,调用签名,构造签名和索引签名(统称为成员)组成。

类和接口类型的引用,数组类型,元组类型,函数类型和构造函数类型都归为对象类型。 TypeScript语言中的多个构造可创建对象类型,包括:

  • 对象类型文字(第3.8.3节)。
  • 数组类型文字(第3.8.4节)。
  • 元组类型文字(第3.8.5节)。
  • 函数类型文字(第3.8.8节)。
  • 构造函数类型文字(第3.8.9节)。
  • 对象文字(第4.5节)。
  • 数组文字(第4.6节)。
  • 函数表达式(第4.10节)和函数声明(6.1)。
  • 由类声明创建的构造函数类型(第8.2.5节)。
  • 由命名空间声明创建的命名空间实例类型(第10.3节)。

3.3.1 命名类型引用

对类和接口类型的类型引用(第3.8.2节)被归类为对象类型。 泛型类和接口类型的类型引用包括类型参数,这些类型参数将替换类或接口的类型参数以产生实际的对象类型。

3.3.2 Array类型

数组类型表示具有常见元素类型的JavaScript数组。 数组类型是从全局命名空间中的通用接口类型“ Array”创建的命名类型引用,其中数组元素类型为类型实参。 数组类型文字(第3.8.4节)提供了创建此类引用的简写符号。

“数组”接口的声明包括属性“长度”和元素类型的数字索引签名,以及其他成员:

interface Array<T> {  length: number;  [x: number]: T;  // Other members
}

数组文字(第4.6节)可用于创建数组类型的值。 例如

var a: string[] = ["hello", "world"];

如果类型可以分配给any []类型(第3.11.4节),则称其为类似数组的类型。

3.3.3 Tuple类型

元组类型表示具有单独跟踪的元素类型的JavaScript数组。 元组类型使用元组类型文字(第3.8.5节)编写。 元组类型将一组数字命名的属性与数组类型的成员结合在一起。 具体来说,元组类型

[ T0, T1, ..., Tn ]

结合属性集:

{  0: T0;  1: T1;  ...  n: Tn;
}

元素类型为元组元素类型的并集类型(第3.4节)的数组类型的成员。

数组文字(第4.6节)可用于创建元组类型的值。 例如:

var t: [number, string] = [3, "three"];
var n = t[0];  // Type of n is number
var s = t[1];  // Type of s is string
var i: number;
var x = t[i];  // Type of x is number | string

可以通过声明从Array 派生并引入数字命名属性的接口来创建命名元组类型。 例如:

interface KeyValuePair<K, V> extends Array<K | V> { 0: K; 1: V; }var x: KeyValuePair<number, string> = [10, "ten"];

如果类型具有数字名称为“ 0”的属性,则称其为类似元组的类型。

3.3.4 Function类型

包含一个或多个调用签名的对象类型称为函数类型。 可以使用函数类型文字(第3.8.8节)或通过在对象类型文字中包含调用签名来编写函数类型。

3.3.5 Constructor类型

包含一个或多个构造签名的对象类型被称为构造函数类型。 构造函数类型可以使用构造函数类型文字(第3.8.9节)或在对象类型文字中包含构造签名来编写。

3.3.6 Members

每个对象类型都由零个或多个以下类型的成员组成:

  • 属性,用于定义给定类型的对象的属性的名称和类型。 属性名称在其类型中是唯一的。
  • 呼叫签名,定义可能的参数列表和返回类型,这些类型与将呼叫操作应用于给定类型的对象相关联。
  • 构造签名,用于定义可能的参数列表和返回类型,这些签名和返回类型与将new运算符应用于给定类型的对象有关。
  • 索引签名,用于定义给定类型的属性的类型约束。 一个对象类型最多可以具有一个字符串索引签名和一个数字索引签名。

属性是公共的,私有的或受保护的,并且是必需的或可选的:

  • 类声明中的属性可以指定为公共,私有或受保护,而在其他上下文中声明的属性始终被视为公共。私有成员只能在其声明的类中访问,如第8.2.2节所述,私有属性仅在子类型和分配兼容性检查中与自身匹配,如第3.11节所述。受保护的成员只能在其声明的类和从其派生的类中进行访问,如第8.2.2节中所述,受保护的属性仅与自身匹配,并在子类型和赋值兼容性检查中覆盖,如第3.11节中所述。
  • 对象类型文字或接口声明中的属性可以指定为必需或可选,而在其他上下文中声明的属性始终被认为是必需的。如3.11.4节中所述,可以从源对象中忽略赋值的目标类型中可选的属性。

通过包含带有字符串文字类型的参数,可以对调用和构造签名进行专门化(第3.9.2.4节)。专用签名用于表示模式,其中某些参数的特定字符串值导致其他参数的类型或函数结果变得更加专用。

3.4 Union类型

联合类型表示可能具有几种不同表示形式之一的值。 联合类型A |的值。 B是A类型或B类型的值。联合类型使用联合类型文字(第3.8.6节)编写。

联合类型包含一组有序的组成类型。 虽然通常是A | B等于B | 答:确定联合类型的调用和构造签名时,构成类型的顺序可能很重要。

联合类型具有以下子类型关系:

  • 如果U中的每个类型都是T的子类型,则并集类型U是T的子类型。
  • 如果T是U中任何类型的子类型,则类型T是联合类型U的子类型。

同样,联合类型具有以下可分配性关系:

  • 如果U中的每个类型都可以分配给T,则可以将联合类型U分配给类型T。
  • 如果T可分配给U中的任何类型,则类型T可分配给并集类型U。

|| 条件运算符(第4.19.7节和4.20节)可能会产生联合类型的值,而数组文字(第4.6节)可能会产生以联合类型作为其元素类型的数组值。

类型防护(第4.24节)可用于将联合类型缩小为更特定的类型。 特别是,类型防护对于将联合类型值范围缩小为非联合类型值很有用。

在这个例子中

var x: string | number;
var test: boolean;
x = "hello";            // Ok
x = 42;                 // Ok
x = test;               // Error, boolean not assignable
x = test ? 5 : "five";  // Ok
x = test ? 0 : false;   // Error, number | boolean not assignable

可以为’x’分配类型为字符串,数字或联合类型字符串的值| 数字,但没有其他任何类型。 要访问“ x”中的值,可以使用类型保护将类型“ x”的类型首先缩小为字符串或数字:

var n = typeof x === "string" ? x.length : x;  // Type of n is number

出于属性访问和函数调用的目的,联合类型的表观成员(第3.11.1节)是其每种构成类型中都存在的成员,而类型是构成类型中各个表观成员的联合。 以下示例说明了根据对象类型创建联合类型时发生的成员类型合并。

interface A {  a: string;  b: number;
}interface B {  a: number;  b: number;  c: number;
}var x: A | B;
var a = x.a;  // a has type string | number
var b = x.b;  // b has type number
var c = x.c;  // Error, no property c in union type

请注意,“ xa”具有联合类型,因为“ a”的类型在“ A”和“ B”中是不同的,而“ xb”只是具有类型号,因为这在“ A”和“ b”中都是“ b”的类型 ‘B’。 还要注意,没有属性“ x.c”,因为只有“ B”具有属性“ c”。

当用作上下文类型时(第4.23节),联合类型具有那些以其任何构成类型存在的成员,而这些类型是构成类型中各个成员的联合。 具体来说,用作上下文类型的并集类型具有第3.11.1节中定义的明显成员,不同的是,特定成员仅需要以一种或多种构成类型而不是所有构成类型存在。

3.5 Intersection类型

交叉点类型表示同时具有多种类型的值。相交类型A和B的值是类型A和类型B的值。相交类型使用相交类型文字(第3.8.7节)编写。

交集类型包含一组有序的组成类型。虽然A和B等效于B&A通常是正确的,但是在确定调用和构造交集类型的签名时,构成类型的顺序可能很重要。

交叉点类型具有以下子类型关系:

  • 如果I中的任何类型是T的子类型,则交集类型I是T的子类型。
  • 如果T是I中每种类型的子类型,则类型T是交点类型I的子类型。

同样,交叉点类型具有以下可分配性关系:

  • 如果I中的任何类型都可以分配给T,则可以将交点类型I分配给类型T。
  • 如果T可分配给I中的每个类型,则类型T可分配给相交类型I。

出于属性访问和函数调用的目的,交集类型的表观成员(第3.11.1节)是存在于其一个或多个组成类型中的那些成员,而类型是组成类型中各个表观成员的交集。以下示例说明了根据对象类型创建交集类型时发生的成员类型合并。

interface A { a: number }
interface B { b: number }var ab: A & B = { a: 1, b: 1 };
var a: A = ab;  // A & B assignable to A
var b: B = ab;  // A & B assignable to Binterface X { p: A }
interface Y { p: B }var xy: X & Y = { p: ab };  // X & Y has property p of type A & Btype F1 = (a: string, b: string) => void;
type F2 = (a: number, b: number) => void;var f: F1 & F2 = (a: string | number, b: string | number) => { };
f("hello", "world");  // Ok
f(1, 2);              // Ok
f(1, "test");         // Error

联合和交集类型运算符可以应用于类型参数。 例如,此功能可用于对合并对象的函数进行建模:

function extend<T, U>(first: T, second: U): T & U {  // Extend first with properties of second
}var x = extend({ a: "hello" }, { b: 42 });
var s = x.a;
var n = x.b;

可以创建交集类型,对于这些交集类型,除null或undefined之外的其他值均不可能。 例如,基本类型(例如字符串和数字)的交集属于此类别。

3.6 Type参数

类型参数表示在通用类型引用或通用函数调用中参数绑定到的实际类型。 类型参数具有约束条件,可为其实际类型参数建立上限。

由于类型参数表示大量不同的类型参数,因此与其他类型相比,类型参数具有某些限制。 特别是,类型参数不能用作基类或接口。

3.6.1 Type Parameter列表

类,接口,类型别名和函数声明可以选择包含在<和>括号中的类型参数列表。对象,函数和构造函数类型文字的调用签名中还允许使用类型参数。

TypeParameters:<TypeParameterList>TypeParameterList:TypeParameterTypeParameterList,TypeParameterTypeParameter:BindingIdentifier Constraint(opt)Constraint:extends Type

类型参数名称必须唯一。如果同一TypeParameterList中的两个或多个类型参数具有相同的名称,则会发生编译时错误。

类型参数的范围扩展到与类型参数列表关联的整个声明,但类中的静态成员声明除外。

类型参数可能具有关联的类型参数约束,该约束为类型实参建立上限。可以在同一类型参数列表内的类型参数约束中引用类型参数,甚至包括出现在类型参数左侧的约束声明。

类型参数T的基本约束定义如下:

  • 如果T没有声明的约束,则T的基本约束为空对象类型{}。
  • 如果T的声明约束是类型参数,则T的基本约束是该类型参数的约束。
  • 否则,T的基本约束是T的声明约束。

在这个例子中

interface G<T, U extends V, V extends Function> { }

“ T”的基本约束是空对象类型,“ U”和“ V”的基本约束是“功能”。

为了确定类型关系(第3.11节),类型参数似乎是其基本约束的子类型。 同样,在属性访问(第4.13节),新操作(第4.14节)和函数调用(第4.15节)中,类型参数似乎具有其基本约束的成员,但没有其他成员。

类型参数直接或间接成为其自身的约束是错误的。 例如,以下两个声明均无效:

interface A<T extends T> { }interface B<T extends U, U extends T> { }

3.6.2 Type Argument列表

泛型类型的类型引用(第3.8.2节)必须包括用尖括号括起来并用逗号分隔的类型参数列表。 类似地,对泛型函数的调用(第4.15节)可以显式包括类型实参列表,而不是依赖于类型推断。

TypeArguments:< TypeArgumentList >
TypeArgumentList:TypeArgumentTypeArgumentList , TypeArgument
TypeArgument:Type

类型参数与所引用的泛型类型或函数的类型参数一一对应。 需要一个类型实参列表为每个对应的类型形参精确指定一个类型实参,并且需要一个约束类型形参的每个类型实参来满足该类型形参的约束。 如果将类型实参替换为类型实参,则该类型实参满足类型参数约束,则该类型实参可分配给约束类型(第3.11.4节)。

给出声明

interface G<T, U extends Function> { }

类型为“ G <A,B>”的类型引用对“ A”没有任何要求,但要求“ B”可分配给“功能”。

将类型实参替换为通用类型或通用签名中的类型参数的过程称为实例化通用类型或签名。 如果提供的类型参数不满足其对应类型参数的约束,则泛型类型或签名的实例化可能会失败。

3.6.3 This类型

每个类和接口都有一个this类型,它表示类或接口的声明中类或接口实例的实际类型。 在类型位置使用关键字this引用this-type。 在类的实例方法和构造函数中,表达式this的类型(4.2节)是类的this类型。

类和接口支持继承,因此,在方法中由此方法表示的实例不一定是包含类的实例,实际上它可能是派生类或接口的实例。 为了对此关系建模,将类或接口的此类型分类为类型参数。 与其他类型参数不同,不可能为this-type显式传递类型参数。 相反,在对类或接口类型的类型引用中,类型引用本身会隐式作为this-type的类型参数传递。 例如:

class A {  foo() {  return this;  }
}class B extends A {  bar() {  return this;  }
}let b: B;
let x = b.foo().bar();  // Fluent pattern works, type of x is B

在上面的b声明中,类型引用B本身作为B的this-type的类型参数传递。 因此,引用的类型是类B的实例,其中所有该类型的实例都被B替换,因此,B的foo方法实际上返回B(与A相对)。

给定类或接口类型C的this-type隐含一个约束,该约束包括对C的类型引用,其中C自己的类型参数作为类型实参传递,并且该类型引用作为this-type的类型实参传递。

3.7 命名类型

类,接口,枚举和类型别名是通过类声明(第8.1节),接口声明(第7.1节),枚举声明(9.1)和类型别名声明(第3.10节)引入的命名类型。类,接口和类型别名可以具有类型参数,然后称为泛型。相反,没有类型参数的命名类型称为非泛型类型。

接口声明仅引入命名类型,而类声明引入命名类型和构造函数,这些函数创建这些命名类型的实现实例。由类和接口声明引入的命名类型只有细微的差别(类不能声明可选成员,接口不能声明私有或受保护的成员),并且在大多数情况下可以互换。特别是,仅具有公共成员的类声明引入的命名类型的功能与接口声明创建的命名类型完全相同。

命名类型通过类型引用(第3.8.2节)进行引用,这些引用指定类型名称,并在适用时指定要替换命名类型的类型参数的类型实参。

从技术上讲,命名类型不是类型-仅引用命名类型。这种区别在泛型类型中尤为明显:泛型类型是“模板”,可以通过编写类型引用来从其创建多个实际类型,这些类型引用提供类型参数来代替泛型类型的类型参数。此替换过程称为实例化泛型类型。只有实例化泛型类型后,它才表示实际类型。

TypeScript具有结构类型系统,因此,通用类型的实例与等效的手动扩展没有区别。例如,给定声明

interface Pair<T1, T2> { first: T1; second: T2; }

类型参考:

Pair<string, Entity>

与类型没有区别:

{ first: string; second: Entity; }

3.8 指定类型

通过引用其关键字或名称或通过编写对象类型文字,数组类型文字,元组类型文字,函数类型文字,构造函数类型文字或类型查询来指定类型。

Type:UnionOrIntersectionOrPrimaryTypeFunctionTypeConstructorTypeUnionOrIntersectionOrPrimaryType:UnionTypeIntersectionOrPrimaryTypeIntersectionOrPrimaryType:IntersectionTypePrimaryTypePrimaryType:ParenthesizedTypePredefinedTypeTypeReferenceObjectTypeArrayTypeTupleTypeTypeQueryThisType
ParenthesizedType:( Type )

当联合,交集,函数或构造函数类型用作数组元素类型时,必须在它们之间加上括号; 交集类型中的联合,函数或构造函数类型周围; 以及联合类型中的函数或构造函数类型。 例如:

(string | number)[]
((x: string) => string) | ((x: number) => number)
(A | B) & (C | D)

以下各节介绍了不同类型的类型符号。

3.8.1 预定义类型

any,number,boolean,string,symbol和void关键字分别引用Any类型和Number,Boolean,String,Symbol和Void基本类型。

PredefinedType:anynumberbooleanstringsymbolvoid

预定义类型关键字是保留的,不能用作用户定义类型的名称。

3.8.2 类型引用

类型引用通过其名称引用命名的类型或类型参数,如果是通用类型,则提供类型参数列表。

TypeReference:TypeName [no LineTerminator here] TypeArgumentsoptTypeName:IdentifierReferenceNamespaceName . IdentifierReferenceNamespaceName:IdentifierReferenceNamespaceName . IdentifierReference

TypeReference由TypeName组成,该TypeName引用命名的类型或类型参数。对泛型类型的引用之后必须是TypeArguments的列表(第3.6.2节)。

TypeName是单个标识符或由点分隔的标识符序列。在类型名称中,除最后一个标识符外,所有标识符均指命名空间,最后一个标识符指代命名类型。

在第2.4节中描述了由单个标识符组成的TypeName的解析。

解析形式为NX的TypeName,其中N为NamespaceName,X为IdentifierReference,首先解析命名空间名称N。如果N的解析成功,并且导出成员集(第10.4和11.3.4.4节)结果命名空间包含命名类型X,然后NX引用该成员。否则,N.X是不确定的。

由单个标识符组成的NamespaceName的解析在2.4节中描述。在命名空间声明(第10.1节)或导入声明(第10.3、11.3.2和11.3.3节)中声明的标识符可以归为命名空间。

解析形式为NX的NamespaceName,其中N为NamespaceName,X为IdentifierReference,首先解析命名空间名称N。如果N的解析成功,并且导出成员集(第10.4和11.3.4.4节)结果命名空间包含一个导出的命名空间成员X,然后NX引用该成员。否则,N.X是不确定的。

需要使用对通用类型的类型引用来为所引用的通用类型的每个类型参数精确指定一个类型参数,并且每个类型参数必须可分配给(第3.11.4节)相应类型参数的约束,否则将产生错误发生。一个例子:

interface A { a: string; }interface B extends A { b: string; }interface C extends B { c: string; }interface G<T, U extends B> {  x: T;  y: U;
}var v1: G<A, C>;               // Ok
var v2: G<{ a: string }, C>;   // Ok, equivalent to G<A, C>
var v3: G<A, A>;               // Error, A not valid argument for U
var v4: G<G<A, B>, C>;         // Ok
var v5: G<any, any>;           // Ok
var v6: G<any>;                // Error, wrong number of arguments
var v7: G;                     // Error, no arguments

类型实参只是一个类型,本身可以是对通用类型的类型引用,如上例中的“ v4”所示。

如3.7节所述,对通用类型G的类型引用表示一种类型,其中G的所有类型参数的出现都已用类型引用中提供的实际类型自变量替换。 例如,上面的’v1’声明等同于:

var v1: {  x: { a: string; }  y: { a: string; b: string; c: string };
};

3.8.3 Object类型文法

对象类型文字通过指定静态地认为在该类型的实例中存在的成员集来定义对象类型。 可以使用接口声明为对象类型文字赋予名称,但否则是匿名的。

ObjectType:{ TypeBodyopt }TypeBody:TypeMemberList ;optTypeMemberList ,optTypeMemberList:TypeMemberTypeMemberList ; TypeMemberTypeMemberList , TypeMemberTypeMember:PropertySignatureCallSignatureConstructSignatureIndexSignatureMethodSignature

对象类型文字的成员被指定为属性,调用,构造,索引和方法签名的组合。 对象类型成员在第3.9节中描述。

3.8.4 Array类型文法

数组类型文字被写为元素类型,后跟一个打开和关闭的方括号。

ArrayType:PrimaryType [no LineTerminator here] [ ]

数组类型文字会引用具有给定元素类型的数组类型(第3.3.2节)。 数组类型文字是在全局命名空间中引用通用接口类型“ Array”的简写形式,其中元素类型作为类型实参。

当联合,交集,函数或构造函数类型用作数组元素类型时,必须将其括在括号中。 例如:

(string | number)[]
(() => string)[]

或者,可以使用“ Array ”符号来编写数组类型。 例如,上述类型等同于

Array<string | number>
Array<() => string>

3.8.5 Tuple类型文法

元组类型文字是作为元素类型的序列编写的,用逗号分隔并括在方括号中。

TupleType:[ TupleElementTypes ]TupleElementTypes:TupleElementTypeTupleElementTypes , TupleElementTypeTupleElementType:Type

元组类型文字引用了元组类型(第3.3.3节)。

3.8.6 Union类型文法

联合类型文字被写为由竖线分隔的一系列类型。

UnionType:UnionOrIntersectionOrPrimaryType | IntersectionOrPrimaryType

联合类型字面量引用联合类型(第3.4节)。

3.8.7 Intersection类型文法

交集类型文字被写为由&分隔的一系列类型。

IntersectionType:IntersectionOrPrimaryType&PrimaryType

交集类型文字会引用交集类型(第3.5节)。

3.8.8 Function类型文法

函数类型文字可指定调用签名的类型参数,常规参数和返回类型。

FunctionType:TypeParametersopt(ParameterListopt)=> Type

函数类型文字是包含单个调用签名的对象类型的简写。 具体来说,是形式的函数类型文字

< T1, T2, ... > ( p1, p2, ... ) => R

与对象类型文字完全等效

{ < T1, T2, ... > ( p1, p2, ... ) : R }

请注意,具有多个调用或构造签名的函数类型不能写为函数类型文字,而必须写为对象类型文字。

3.8.9 Constructor类型文法

构造函数类型文字可指定构造签名的类型参数,常规参数和返回类型。

ConstructorType:new TypeParametersopt(ParameterListopt)=> Type

构造函数类型文字是包含单个构造签名的对象类型的简写。 具体来说,是以下形式的构造函数类型文字

new < T1, T2, ... > ( p1, p2, ... ) => R

与对象类型文字完全等效

{ new < T1, T2, ... > ( p1, p2, ... ) : R }

请注意,具有多个构造签名的构造函数类型不能写为构造函数类型文字,而必须写为对象类型文字。

3.8.10 类型查询

类型查询获取表达式的类型。

TypeQuery:typeof TypeQueryExpressionTypeQueryExpression:IdentifierReferenceTypeQueryExpression . Identifiername

类型查询由关键字typeof及其后的表达式组成。 该表达式仅限于单个标识符或由句点分隔的标识符序列。 该表达式被作为标识符表达式(第4.3节)或属性访问表达式(第4.13节)处理,其扩展类型(第3.12节)成为结果。 与其他静态类型构造类似,类型查询将从生成的JavaScript代码中删除,并且不增加运行时开销。

类型查询对于捕获由各种构造(例如对象文字,函数声明和命名空间声明)生成的匿名类型很有用。 例如:

var a = { x: 10, y: 20 };
var b: typeof a;

上面的’b’与’a’具有相同的类型,即{x:number; y:数字; }。

如果声明包含类型注释,该注释通过类型查询的循环路径或包含类型查询的类型引用引用要声明的实体,则结果类型为Any类型。 例如,以下所有变量的类型都为“任意”:

var c: typeof c;
var d: typeof e;
var e: typeof d;
var f: Array<typeof f>;

但是,如果类型查询的循环路径包括至少一个ObjectType,FunctionType或ConstructorType,则该构造表示递归类型:

var g: { x: typeof g; };
var h: () => typeof h;

这里,“ g”和“ g.x”具有相同的递归类型,同样,“ h”和“ h()”具有相同的递归类型。

3.8.11 This类型推导

this关键字用于引用类或接口的this-type(第3.6.3节)。

ThisType:this

ThisType的含义取决于最接近的封闭函数FunctionDeclaration,FunctionExpression,PropertyDefinition,ClassElement或TypeMember,称为ThisType的根声明,如下所示:

  • 当根声明是类的实例成员或构造函数时,ThisType引用该类的this-type。
  • 当根声明是接口类型的成员时,ThisType引用该接口的此类型。
  • 否则,ThisType是错误。

注意,为了避免歧义,不可能在嵌套对象类型文字中引用此类的此类或接口。 在这个例子中

interface ListItem {  getHead(): this;  getTail(): this;  getHeadAndTail(): { head: this, tail: this };  // Error
}

最后一行的this引用是错误的,因为其根声明不是类或接口的成员。 在对象类型文字中引用外部类或接口的此类型的推荐方法是声明中间泛型并将其作为类型参数传递。 例如:

type HeadAndTail<T> = { head: T, tail: T };interface ListItem {  getHead(): this;  getTail(): this;  getHeadAndTail(): HeadAndTail<this>;
}

3.9 指定成员

对象类型文字(第3.8.3节)的成员被指定为属性,调用,构造,索引和方法签名的组合。

3.9.1 属性签名

属性签名声明属性成员的名称和类型。

PropertySignature:PropertyName?opt TypeAnnotationoptTypeAnnotation::Type

属性签名的PropertyName(2.2.2)在其包含类型内必须是唯一的,并且如果它是计算所得的属性名称(2.2.3),则必须表示一个众所周知的符号。 如果属性名称后面带有问号,则该属性为可选。 否则,该属性是必需的。

如果属性签名省略了TypeAnnotation,则假定为Any类型。

3.9.2 Call签名

呼叫签名定义类型参数,参数列表和返回类型,该类型参数与对包含类型的实例执行呼叫操作(第4.15节)相关。 通过定义多个不同的呼叫签名,一种类型可能会使呼叫操作超载。

CallSignature:TypeParametersopt(ParameterListopt)TypeAnnotationopt

包含TypeParameters(第3.6.1节)的呼叫签名称为通用呼叫签名。 相反,没有TypeParameters的呼叫签名称为非通用呼叫签名。

调用签名不仅是对象类型文字的成员,还出现在方法签名(第3.9.5节),函数表达式(第4.10节)和函数声明(第6.1节)中。

包含调用签名的对象类型被称为函数类型。

3.9.2.1 Type参数

调用签名中的类型参数(第3.6.1节)提供了一种在调用操作中表达参数与返回类型之间关系的机制。例如,签名可能会引入类型参数,并将其用作参数类型和返回类型,实际上是描述一个函数,该函数返回与其参数相同类型的值。

类型参数可以在引入它们的呼叫签名的参数类型和返回类型注释中引用,但不能在类型参数约束中引用。

用于呼叫签名类型参数的类型参数(第3.6.2节)可以在调用操作中明确指定,或者在可能的情况下,可以从调用中的常规参数的类型中推断出类型参数(第4.15.2节)。特定类型参数集的通用调用签名的实例化是通过将每个类型参数替换为其对应的类型参数而形成的调用签名。

下面是带有类型参数的呼叫签名的一些示例。

一个函数,它接受任何类型的参数,并返回相同类型的值:

<T>(x: T): T

该函数接受两个相同类型的值,并返回该类型的数组:

<T>(x: T, y: T): T[]

该函数接受两个不同类型的参数,并返回具有这些类型的属性’x’和’y’的对象:

<T, U>(x: T, y: U): { x: T; y: U; }

一个采用一个类型的数组和一个函数参数的函数,返回另一种类型的数组,其中该函数参数采用第一个数组元素类型的值,并返回第二个数组元素类型的值:

<T, U>(a: T[], f: (x: T) => U): U[]

3.9.2.2 参数列表

签名的参数列表包含零个或多个必需参数,然后是零个或多个可选参数,最后是一个可选的rest参数。

ParameterList:RequiredParameterListOptionalParameterListRestParameterRequiredParameterList,OptionalParameterListRequiredParameterList,RestParameterOptionalParameterList,RestParameterRequiredParameterList,OptionalParameterList,RestParameterRequiredParameterList:RequiredParameterRequiredParameterList,RequiredParameterRequiredParameter:AccessibilityModifieropt BindingIdentifierOrPattern TypeAnnotationoptBindingIdentifier:StringLiteralAccessibilityModifier:publicprivateprotectedBindingIdentifierOrPattern:BindingIdentifierBindingPatternOptionalParameterList:OptionalParameterOptionalParameterList,OptionalParameterOptionalParameter:AccessibilityModifieropt BindingIdentifierOrPattern? TypeAnnotationoptAccessibilityModifieropt BindingIdentifierOrPatternTypeTypeAnnotationoptInitializerBindingIdentifier? : StringLiteralRestParameter:... BindingIdentifier TypeAnnotationopt

参数声明可以指定标识符或绑定模式(5.2.2)。参数声明中指定的标识符和参数列表中的绑定模式在该参数列表中必须是唯一的。

签名中参数的类型确定如下:

  • 如果声明中包含类型注释,则参数为该类型。
  • 否则,如果声明包含初始化程序表达式(仅当参数列表与函数体一起出现时才允许使用),则参数类型为初始化程序表达式类型的扩展形式(第3.12节)。
  • 否则,如果声明指定了绑定模式,则参数类型是该绑定模式的隐含类型(第5.2.3节)。
  • 否则,如果参数是rest参数,则参数类型为any []。
  • 否则,参数类型为任何。

仅当参数出现在ConstructorImplementation的参数列表(第8.3.1节)中且未指定BindingPattern时,才允许其包含public,private或protected修饰符。

rest参数的类型注释必须表示数组类型。

当参数类型注释指定字符串文字类型时,包含的签名是专用签名(第3.9.2.4节)。不允许将专用签名与函数体结合使用,即FunctionExpression,FunctionImplementation,MemberFunctionImplementation和ConstructorImplementation语法生成不允许使用字符串文字类型的参数。

可以通过在参数名称或绑定模式后加上问号(?)或包含初始化程序来将参数标记为可选。仅当参数列表与函数主体一起出现时才允许使用初始化程序(包括绑定属性或元素初始化程序),即仅在FunctionExpression,FunctionImplementation,MemberFunctionImplementation或ConstructorImplementation语法生成中。

TODO:更新以反映绑定参数在实现签名中不能为可选。

TODO:更新以反映必需的参数支持初始化程序。

3.9.2.3 Return类型

呼叫签名的返回类型注释(如果存在)指定由呼叫操作计算和返回的值的类型。 void返回类型注释用于指示函数没有返回值。

如果在没有函数主体的上下文中发生没有返回类型注释的调用签名,则假定返回类型为Any类型。

当在具有函数主体(特别是函数实现,成员函数实现或成员访问器声明)的上下文中发生没有返回类型注释的调用签名时,将如本节所述从函数主体中推断出返回类型。 6.3。

3.9.2.4 特定签名

当参数类型注释指定字符串文字类型(第3.2.9节)时,包含的签名被视为专用签名。 专用签名用于表示模式,其中某些参数的特定字符串值会导致其他参数的类型或函数结果变得更加专用。 例如,声明

interface Document {  createElement(tagName: "div"): HTMLDivElement;   createElement(tagName: "span"): HTMLSpanElement;  createElement(tagName: "canvas"): HTMLCanvasElement;  createElement(tagName: string): HTMLElement;
}

声明使用字符串文字“ div”,“ span”和“ canvas”调用“ createElement”分别返回类型为“ HTMLDivElement”,“ HTMLSpanElement”和“ HTMLCanvasElement”的值,并使用所有其他字符串表达式返回“ HTMLElement”类型的值。

编写上面的声明之类的重载声明时,最后列出非专用签名非常重要。这是因为重载决议(第4.15.1节)按声明顺序处理候选项,并选择匹配的第一个候选项。

对象类型中的每个专用调用或构造签名必须至少可分配给同一对象类型中的至少一个非专用调用或构造签名(如果一个对象类型仅包含A,则认为一个调用签名A可分配给另一个调用签名B可分配给仅包含B)的对象类型。例如,上面示例中的’createElement’属性的类型包含三个专用签名,所有这些签名都可以分配给该类型中的非专用签名。

3.9.3 Construct签名

构造签名定义了与将new运算符(第4.14节)应用于包含类型的实例相关联的参数列表和返回类型。 通过定义具有不同参数列表的多个构造签名,类型可以使新操作过载。

ConstructSignature:new TypeParametersopt(ParameterListopt)TypeAnnotationopt

构造签名的类型参数,参数列表和返回类型应遵循与调用签名相同的规则。

包含构造签名的类型被称为构造函数类型。

3.9.4 索引签名

索引签名为包含类型中的属性定义类型约束。

IndexSignature:[BindingIdentifier:string] TypeAnnotation[BindingIdentifier:number] TypeAnnotation

索引签名有两种:

  • 使用索引类型字符串指定的字符串索引签名为所有属性和包含类型中的数字索引签名定义类型约束。具体地说,在类型为T的字符串索引签名的类型中,所有属性和数字索引签名必须具有可分配给T的类型。
  • 使用索引类型编号指定的数字索引签名定义了包含类型中所有数字命名属性的类型约束。具体地说,在具有数字类型T的数字索引签名的类型中,所有以数字命名的属性必须具有可分配给T的类型。
    以数字命名的属性是其名称是有效数字文字的属性。具体来说,名称为N的属性的ToString(ToNumber(N))与N相同,其中ToString和ToNumber是ECMAScript规范中定义的抽象操作。

一个对象类型最多可以包含一个字符串索引签名和一个数字索引签名。

索引签名会影响对类型的确定,该确定是通过对包含类型的实例应用括号表示法属性访问而产生的,如第4.13节所述。

3.9.5 方法签名

方法签名是用于声明函数类型属性的简写。

MethodSignature:PropertyName?opt CallSignature

如果PropertyName是计算的属性名称(2.2.3),则必须指定一个众所周知的符号。 如果PropertyName后面带有问号,则该属性为可选。 否则,该属性是必需的。 仅对象类型文字和接口可以声明可选属性。

表单的方法签名

f < T1, T2, ... > ( p1, p2, ... ) : R

等同于属性声明

f : { < T1, T2, ... > ( p1, p2, ... ) : R }

文字类型可以通过声明多个具有相同名称但参数列表不同的方法签名来重载方法。 必须全部重载(省略问号),或者全部重载是可选的(包括问号)。 一组重载的方法签名对应于单个属性的声明,该声明的类型由等效的一组调用签名组成。 特别

f < T1, T2, ... > ( p1, p2, ... ) : R ;
f < U1, U2, ... > ( q1, q2, ... ) : S ;
...

相当于

f : {  < T1, T2, ... > ( p1, p2, ... ) : R ;  < U1, U2, ... > ( q1, q2, ... ) : S ;  ...
} ;

在下面的对象类型示例中

{  func1(x: number): number;         // Method signature  func2: (x: number) => number;     // Function type literal  func3: { (x: number): number };   // Object type literal
}

属性“ func1”,“ func2”和“ func3”都是相同的类型,即具有单个调用签名并带有数字并返回数字的对象类型。 同样,在对象类型中

{  func4(x: number): number;  func4(s: string): string;  func5: {  (x: number): number;  (s: string): string;  };
}

属性“ func4”和“ func5”具有相同的类型,即具有两个调用签名的对象类型,分别带有并返回数字和字符串。

3.10 类型别名

类型别名声明在包含的声明空间中引入了类型别名。

TypeAliasDeclaration:type BindingIdentifier TypeParametersopt = Type;

类型别名用作类型别名声明中指定的类型的别名。与总是引入命名对象类型的接口声明不同,类型别名声明可以为任何类型的类型引入名称,包括基本类型,联合类型和交集类型。

类型别名可以选择具有类型参数(第3.6.1节),这些类型参数用作在类型引用中引用类型别名时要提供的实际类型的占位符。具有类型参数的类型别名称为通用类型别名。通用类型别名声明的类型参数在范围内,并且可以在别名类型中引用。

使用类型引用(3.8.2)引用类型别名。对通用类型别名的类型引用使用给定的类型参数生成别名类型的实例化。编写对非通用类型别名的引用与编写别名类型本身具有完全相同的效果,而编写对通用类型别名的引用与编写别名类型的结果实例化具有完全相同的效果。

类型别名声明的BindingIdentifier可能不是预定义的类型名称之一(第3.8.1节)。

类型别名中指定的类型依赖于该类型别名是错误的。类型具有以下依赖性:

  • 类型别名直接取决于其别名的类型。
  • 类型引用直接取决于所引用的类型以及每个类型参数(如果有)。
  • 并集或交集类型直接取决于每种构成类型。
  • 数组类型直接取决于其元素类型。
  • 元组类型直接取决于其每个元素类型。
  • 类型查询直接取决于引用实体的类型。

给定这个定义,一个类型所依赖的完整类型集就是直接依赖关系的传递闭包。请注意,对象类型文字,函数类型文字和构造函数类型文字不依赖于其中引用的类型,因此允许通过类型别名循环引用自身。

类型别名声明的一些示例:

type StringOrNumber = string | number;
type Text = string | { text: string };
type NameLookup = Dictionary<string, Person>;
type ObjectStatics = typeof Object;
type Callback<T> = (data: T) => void;
type Pair<T> = [T, T];
type Coordinates = Pair<number>;
type Tree<T> = T | { left: Tree<T>, right: Tree<T> };

接口类型与对象类型文字的类型别名有很多相似之处,但是由于接口类型提供了更多的功能,因此它们通常比类型别名更可取。 例如,接口类型

interface Point {  x: number;  y: number;
}

可以写为类型别名

type Point = {  x: number;  y: number;
};

但是,这样做意味着失去了以下功能:

  • 可以在extend或Implements子句中命名接口,但是不能为对象类型文字提供类型别名。
  • 接口可以具有多个合并的声明,但是对象类型文字的类型别名不能。

3.11 Type关系

TypeScript中的类型具有身份,子类型,超类型和分配兼容性关系,如以下各节所定义。

3.11.1 明显成员

类型的表观成员是在子类型,超类型和分配兼容性关系以及属性访问(4.13节),新操作(4.14节)和函数调用(4.15节)的类型检查中观察到的成员。类型的外观成员确定如下:

  • 基本类型Number和所有枚举类型的明显成员是全局接口类型’Number’的明显成员。
  • 基本类型布尔值的表观成员是全局接口类型“布尔值”的表观成员。
  • 基本类型String和所有字符串文字类型的明显成员是全局接口类型’String’的明显成员。
  • 类型参数的表观成员是该类型参数的约束的表观成员(第3.6.1节)。
  • 对象类型T的表观成员是以下各项的组合:
    • T的已声明和/或继承的成员。
    • 全局接口类型“对象”的属性不会被T中具有相同名称的属性隐藏。
    • 如果T具有一个或多个调用或构造签名,则全局接口类型’Function’的属性不会被T中具有相同名称的属性隐藏。
  • 联合类型U的表观成员确定如下:
    • 当U的所有构成类型都具有名为N的外观属性时,U会具有各自属性类型的并集类型的外观属性N。
    • 当U的所有组成类型均具有带有参数列表P的明显呼叫签名时,U具有带有参数列表P和作为各自返回类型的并集的返回类型的明显呼叫签名。呼叫签名以与第一种构成类型相同的顺序出现。
    • 当U的所有构成类型均具有带有参数列表P的明显构造体签名时,U具有带有参数列表P的明显构造体签名以及作为各个返回类型的并集的返回类型。构造签名的出现顺序与第一种组成类型相同。
    • 当U的所有构成类型均具有表观字符串索引签名时,U具有各自字符串索引签名类型的并集类型的表观字符串索引签名。
    • 当U的所有构成类型均具有表观数字索引签名时,U具有各自数字索引签名类型的并集类型的表观数字索引签名。
  • 相交类型I的外观成员确定如下:
    • 当I的多个构成类型之一具有名为N的外观属性时,我具有相应属性类型的交集类型的名为N的外观属性。
    • 当I的一个或多个组成类型具有呼叫签名S时,I则具有明显的呼叫签名S。这些签名按I中组成类型的顺序按每个组成类型的签名的顺序进行排序。
    • 当I的一个或多个构成类型具有构造签名S时,我具有明显的构造签名S。这些签名按I中构成类型的顺序按每种构成类型的签名的顺序排列。
    • 当I的一种或多种组成类型具有明显的字符串索引签名时,我具​​有相应的字符串索引签名类型的交集类型的明显的字符串索引签名。
    • 当我的一个或多个构成类型具有明显的数字索引签名时,我具​​有相应的数字索引签名类型的交集类型的明显的数字索引签名。

如果类型不是上述之一,则认为它没有明显的成员。

实际上,类型的外观成员使其成为“对象”或“功能”接口的子类型,除非该类型定义的成员与“对象”或“功能”接口的成员不兼容(例如,如果type定义的属性与“ Object”或“ Function”接口中的属性同名,但其类型不是“ Object”或“ Function”接口中的子类型。

一些例子:

var o: Object = { x: 10, y: 20 };         // Ok
var f: Function = (x: number) => x * x;   // Ok
var err: Object = { toString: 0 };        // Error

最后一个赋值是一个错误,因为对象文字的’toString’方法与’Object’的方法不兼容。

3.11.2 Type和Member类型

两种类型被认为是相同的

  • 它们都是Any类型,
  • 它们是相同的原始类型,
  • 它们是相同的类型参数
  • 它们是具有相同构成类型集的联合类型,或者
  • 它们是具有相同构成类型集的交集类型,或者
  • 它们是具有相同成员集的对象类型。

当两个成员被认为相同时

  • 它们是具有相同名称,可选性和类型的公共属性,
  • 它们是源自同一声明且具有相同类型的私有或受保护属性,
  • 它们是相同的呼叫签名
  • 它们是相同的构造签名,或者
  • 它们是具有相同类型,相同类型的索引签名。

当两个调用或构造签名具有相同数量的类型参数且具有相同类型参数约束时,并且在用type Any替换签名所引入的类型参数之后,将相同数量的相同类型参数(必需,可选或其余)视为相同)和类型,以及相同的返回类型。

请注意,除了具有私有或受保护成员的原始类型和类之外,确定身份的是结构的类型而非命名。另外,请注意,在确定签名身份时,参数名称并不重要。

仅当私有属性和受保护属性起源于相同的声明并且具有相同的类型时,它们才匹配。如果两个不同的类型是对同一泛型类的单独参数化引用,则两个不同的类型可能包含源自同一声明的属性。在这个例子中

class C<T> { private x: T; }interface X { f(): string; }interface Y { f(): string; }var a: C<X>;
var b: C<Y>;

变量’a’和’b’具有相同的类型,因为对C的两个类型引用都创建了具有源自同一声明的私有成员’x’的类型,并且因为两个私有’x’成员具有 类型参数“ X”和“ Y”被替换后,成员将完全相同。

3.11.3 子类型和超类型

如果S不具有相对于T的多余属性(3.11.5),并且满足以下条件之一,则S是T类型的子类型,并且T是S的超类型。

  • S和T是相同的类型。
  • T是Any类型。
  • S是未定义的类型。
  • S是Null类型,T不是Undefined类型。
  • S是枚举类型,T是原始类型Number。
  • S是字符串文字类型,而T是原始类型String。
  • S是联合类型,S的每个构成类型是T的子类型。
  • S是交集类型,S的至少一个组成类型是T的子类型。
  • T是并集类型,S是T的至少一个组成类型的子类型。
  • T是交集类型,S是T的每个组成类型的子类型。
  • S是类型参数,S的约束是T的子类型。
  • S是对象类型,交集类型,枚举类型或Number,Boolean或String基本类型,T是对象类型,并且对于T中的每个成员M,下列条件之一为真:
    • M是一个属性,S是一个明显的属性N,其中

      • M和N具有相同的名称,
      • N的类型是M的子类型,
      • 如果M是必需属性,则N也是必需属性,并且
      • M和N都是公共的,M和N都是私有的,并且起源于同一声明,M和N都受保护,起源于同一声明,或者M受保护,并且N声明于从该类继承的类中M被声明。
    • M是非专业的调用或构造签名,S是明显的调用或构造签名N,其中,当使用类型Any作为M和N声明的所有类型参数(如果有)的类型实参来实例化M和N时,
      • 签名属于同一种类(调用或构造),
      • M具有一个休息参数,或者N中非可选参数的数量小于或等于M中的参数总数,
      • 对于两个签名中都存在的参数位置,N中的每个参数类型都是M中相应参数类型的子类型或超类型,并且
      • M的结果类型是Void,或者N的结果类型是M的子类型。
    • M是类型U的字符串索引签名,而U是Any类型,或者S具有明显类型的字符串索引签名,该类型是U的子类型。
    • M是类型U的数字索引签名,U是Any类型,或者S具有作为U的子类型的类型的表观字符串或数字索引签名。

比较调用或构造签名时,参数名称将被忽略,其余参数对应于其余参数元素类型的可选参数的无限制扩展。

注意,在确定子类型和超类型关系时,专用的调用和构造签名(第3.9.2.4节)并不重要。

另请注意,类型参数不被视为对象类型。因此,类型参数T的唯一子类型是T本身以及直接或间接约束为T的其他类型参数。

3.11.4 赋值兼容性

在某些情况下,类型必须与赋值兼容,例如赋值语句中的表达式和变量类型以及函数调用中的参数和参数类型。

如果S相对于T(3.11.5)没有多余的属性并且满足以下条件之一,则S可分配给T类型,T可从S分配给T:

  • S和T是相同的类型。
  • S或T是Any类型。
  • S是未定义的类型。
  • S是Null类型,T不是Undefined类型。
  • S或T是枚举类型,另一个是原始类型Number。
  • S是字符串文字类型,而T是原始类型String。
  • S是联合类型,并且S的每个组成类型都可以分配给T。
  • S是交叉点类型,并且S的至少一个组成类型可分配给T。
  • T是联合类型,并且S可分配给T的至少一个组成类型。
  • T是交集类型,并且S可分配给T的每个组成类型。
  • S是类型参数,并且S的约束可分配给T。
  • S是对象类型,交集类型,枚举类型或Number,Boolean或String基本类型,T是对象类型,并且对于T中的每个成员M,下列条件之一为真:
    • M是一个属性,S是一个明显的属性N,其中

      • M和N具有相同的名称,
      • N的类型可分配给M的类型,
      • 如果M是必需属性,则N也是必需属性,并且
      • M和N都是公共的,M和N都是私有的,并且起源于同一声明,M和N都受保护,起源于同一声明,或者M受保护,并且N声明于从该类继承的类中M被声明。
    • M是可选属性,S没有与M相同名称的明显属性。
    • M是非专业的调用或构造签名,S是明显的调用或构造签名N,其中,当使用类型Any作为M和N声明的所有类型参数(如果有)的类型实参来实例化M和N时,
      • 签名属于同一种类(调用或构造),
      • M具有一个休息参数,或者N中非可选参数的数量小于或等于M中的参数总数,
      • 对于两个签名中都存在的参数位置,N中的每个参数类型都可以分配给M中的相应参数类型或从M中的相应参数类型分配,并且
      • M的结果类型为Void或N的结果类型可分配给M的结果类型。
    • M是类型U的字符串索引签名,而U是Any类型,或者S具有可分配给U的类型的表观字符串索引签名。
    • M是类型U的数字索引签名,而U是Any类型,或者S具有可分配给U的明显的字符串或数字索引签名。

比较调用或构造签名时,参数名称将被忽略,其余参数对应于其余参数元素类型的可选参数的无限制扩展。

注意,在确定分配兼容性时,专用的调用和构造签名(第3.9.2.4节)并不重要。

分配兼容性和子类型化规则的不同之处仅在于:

  • 任何类型均可分配给所有类型,但不能分配给所有类型,
  • 基本类型Number可以分配给所有枚举类型,但不能为其子类型,并且
  • 没有特定属性的对象类型可以分配给该属性是可选的对象类型。

分配兼容性规则意味着,在分配值或传递参数时,可选属性必须要么存在且具有兼容类型,要么根本不存在。例如:

function foo(x: { id: number; name?: string; }) { }foo({ id: 1234 });                 // Ok
foo({ id: 1234, name: "hello" });  // Ok
foo({ id: 1234, name: false });    // Error, name of wrong type
foo({ name: "hello" });            // Error, id required but missing

3.11.5 额外属性

子类型和分配兼容性关系要求源类型相对于其目标类型没有多余的属性。此检查的目的是检测对象文字中过多或拼写错误的属性。

如果满足以下条件,则认为源类型S相对于目标类型T具有多余的属性

  • S是如下定义的新鲜对象文字类型,并且
  • S具有T中不期望的一个或多个属性。

如果满足以下条件之一,则可以认为类型T是期望的属性P:

  • T不是对象,并集或交集类型。
  • T是对象类型,并且
    • T具有与P同名的属性,
    • T具有字符串或数字索引签名,
    • T没有属性,或者
    • T是全局类型“对象”。
  • T是联合或交集类型,并且在T的至少一种构成类型中期望P。

为对象文字推断的类型(如第4.5节中所述)被认为是新鲜的对象文字类型。当对象文字类型被扩展(3.12)或类型断言中的表达式类型(4.16)时,新鲜度消失。

考虑以下示例:

interface CompilerOptions {  strict?: boolean;  sourcePath?: string;  targetPath?: string;
}var options: CompilerOptions = {  strict: true,  sourcepath: "./src",  // Error, excess or misspelled property  targetpath: "./bin"   // Error, excess or misspelled property
};

'CompilerOptions’类型仅包含可选属性,因此,无需检查多余的属性,任何对象文字都可以分配给’options’变量(因为拼写错误的属性将被视为其他名称的多余属性)。

如果期望有多余的属性,则可以将索引签名添加到目标类型以指示意图:

interface InputElement {  name: string;  visible?: boolean;  [x: string]: any;            // Allow additional properties of any type
}var address: InputElement = {  name: "Address",  visible: true,  help: "Enter address here",  // Allowed because of index signature  shortcut: "Alt-A"            // Allowed because of index signature
};

3.11.6 上下文签名实例化

在函数调用中进行类型实参推断期间(第4.15.2节),在某些情况下,有必要在参数的非泛型调用签名的上下文中实例化实参表达式的泛型调用签名,以便可以进行进一步的推断。 。 在非通用呼叫签名B的上下文中实例化通用呼叫签名A,如下所示:

  • 使用3.11.7中描述的过程,针对两个签名中存在的那些参数位置,从B中的每个参数类型到A中的对应参数类型,推导A的类型参数,其中其余参数对应于可选参数的无穷扩展 其余参数元素类型的所有参数。
  • 每个类型参数的推断类型参数是对该类型参数进行的推理的并集类型。 但是,如果联合类型不满足type参数的约束,则推断的type参数将成为约束。

3.11.7 类型推导

在某些情况下,对给定类型参数集的推断是从一个类型S(在其中不出现这些类型参数)到另一个类型T(在其中发生这些类型参数)进行的。推论包括为每个类型参数收集的一组候选类型参数。推理过程将S和T递归关联,以收集尽可能多的推理:

  • 如果T是对其进行推断的类型参数之一,则将S添加到该类型参数的推断集合中。
  • 否则,如果S和T是对相同泛型的引用,则从S中的每个类型实参到T中的每个对应的类型实参进行推断。
  • 否则,如果S和T是元素数相同的元组类型,则从S中的每个元素类型到T中的每个对应元素类型进行推断。
  • 否则,如果T是联合或相交类型:
    • 首先,从S到T中的每个组成类型进行推断,这不仅仅是简单地对其进行推断的类型参数之一。
    • 如果第一步没有产生推论,则如果T是联合类型,并且T中恰好一个构成类型只是对其进行推论的类型参数,则从S对该类型参数进行推论。
  • 否则,如果S是联合或交集类型,则从S到T中的每个组成类型进行推断。
  • 否则,如果S和T是对象类型,则对于T中的每个成员M:
    • 如果M是一个属性,并且S包含与M同名的属性N,则从N的类型到M的类型进行推断。
    • 如果M是呼叫签名,并且S中存在对应的呼叫签名N,则将Any类型实例化为每个类型参数(如果有)的参数,并从N中的参数类型推断出 M中的对应参数类型对于两个签名中都存在的位置,以及从N的返回类型到M的返回类型。
    • 如果M是结构签名,并且S中存在对应的结构签名N,则将Any类型实例化为每个类型参数(如果有)的参数,并从N中的参数类型推导到M中的对应参数类型对于两个签名中都存在的位置,以及从N的返回类型到M的返回类型。
    • 如果M是字符串索引签名,而S包含字符串索引签名N,则从N的类型到M的类型进行推断。
    • 如果M是数字索引签名,并且S包含数字索引签名N,则从N的类型到M的类型进行推断。
    • 如果M是数字索引签名,并且S包含字符串索引签名N,则从N的类型到M的类型进行推断。

比较调用或构造签名时,S中的签名按声明顺序成对地按T配对成相同类型的签名。如果S和T在给定类型的签名中具有不同的编号,则将忽略较长列表的声明顺序中多余的第一个签名。

TODO:更新以反映改进的联合和相交类型推断。

3.11.8 递归类型

类和接口可以在其内部结构中引用自己,从而有效地创建具有无限嵌套的递归类型。 例如,类型

interface A { next: A; }

包含“下一个”属性的无限嵌套序列。 这样的类型是完全有效的,但是在确定类型关系时需要特殊对待。 具体来说,当比较给定关系(身份,子类型或可分配性)的类型S和T时,假定所讨论的关系对于相同S和T的每个直接或间接嵌套出现都是正确的(其中,相同意味着 在相同的声明中,并且(如果适用)具有相同的类型参数)。 例如,考虑上面的“ A”和下面的“ B”之间的身份关系:

interface B { next: C; }interface C { next: D; }interface D { next: B; }

为了确定“ A”和“ B”是否相同,首先比较“ A”和“ C”类型的“下一个”属性。 这导致比较类型“ A”和“ D”的“下一个”属性,从而导致比较类型“ A”和“ B”的“下一个”属性。 由于已经比较了“ A”和“ B”,因此根据定义,这种关系是正确的。 这进而导致其他比较为真,因此最终结果为真。

当使用相同的技术比较泛型类型引用时,两个类型引用源自相同的声明并且具有相同的类型参数,则它们被认为是相同的。

在某些情况下,以递归方式直接或间接引用自身的泛型类型可能会导致无数种不同的实例化。 例如,在类型

interface List<T> {  data: T;  next: List<T>;  owner: List<List<T>>;
}

'List '具有类型’List <List >‘的成员’所有者’,其成员具有’List <List <List >>‘类型的成员’所有者’ 类型为“列表<列表<列表<列表<列表>>>”的“所有者”,依此类推,是无限的。 由于类型关系是从结构上确定的,可能会探究组成类型的全部深度,因此,为了确定涉及无限扩展泛型类型的类型关系,编译器可能有必要在某个点终止递归,并假设不会进行进一步探索 改变结果。

3.12 加宽类型

在某些情况下,TypeScript从上下文推断类型,从而减轻了程序员显式指定明显类型的需求。 例如

var name = "Steve";

将“名称”的类型推断为String基本类型,因为这是用于初始化它的值的类型。 从表达式中推断变量,属性或函数的类型时,源类型的扩展形式将用作目标的推断类型。 类型的加宽形式是将所有出现的Null和Undefined类型替换为any类型的类型。

以下示例显示了扩展类型以产生推断的变量类型的结果。

var a = null;                 // var a: any
var b = undefined;            // var b: any
var c = { x: 0, y: null };    // var c: { x: number, y: any }
var d = [ null, undefined ];  // var d: any[]

[TypeScript] 编程实践之1: Google的TypeScript代码风格3:类型相关推荐

  1. [TypeScript] 编程实践之1: Google的TypeScript代码风格2:基本概念

    TypeScript语言规范 2 基本概念 2.1 语法约定 2.2 命名 2.2.1 保留字 2.2.2 属性命名 2.2.3 计算属性命名 2.3 声明 2.4 范围 2 基本概念 本文档的其余部 ...

  2. [Javascript] 编程实践之1: Google的Javascript代码风格2:源文件基础

    Google的Javascript风格指南 2 源文件基础 2.1 文件命名 2.2 文件编码:UTF-8 2.3 特殊字符 2.3.1 空格字符 2.3.2 特殊转义序列 2.3.3 非ASCII字 ...

  3. 数据简化社区Google和Linux代码风格指南(附PDF公号发“代码风格”下载)

    数据简化社区Google和Linux代码风格指南(附PDF公号发"代码风格"下载) 秦陇纪2019代码类 数据简化DataSimp 昨天 数据简化DataSimp导读:数据简化社区 ...

  4. Google内部 Python 代码风格指南(中文版)

    文末有干货 "Python高校",马上关注 真爱,请置顶或星标 这是一位大佬翻译的Google Python代码风格指南,很全面.可以作为公司的code review 标准,也可以 ...

  5. 快快快收藏!!Google内部Python代码风格指南(中文版)

    ????????关注后回复 "进群" ,拉你进程序员交流群???????? 来源丨菜鸟学Python 这是一位大佬翻译的Google Python代码风格指南,很全面.可以作为公司 ...

  6. UNIX-LINUX编程实践教程-第三章-实例代码注解-ls2

    一 问题 对ls1的功能进行扩展,以达到类似ll命令的功能. 二 分析 在ls1中,我们利用readdir()函数和dirent结构体来获得目标文件夹内的文件名(dirent->d_name). ...

  7. 小程序 Typescript 最佳实践

    小程序结合TypeScript开发,如果用第三方框架,首选Taro已完美支持.但是如果你选择原生开发,那么下面的这份实践可能会帮到你. 小程序 Typescript 最佳实践 使用 gulp 构建(支 ...

  8. 【编程实践】Google Guava 极简教程

    前言 Guava 工程包含了若干被 Google 的 Java 项目广泛依赖 的核心库,我们希望通过此文档为 Guava 中最流行和最强大的功能,提供更具可读性和解释性的说明. 适用人群 本教程是基础 ...

  9. 敏捷开发“松结对编程”实践之六:大型团队篇|后记(大型研发团队,学习型团队,139团队,师徒制度,人员招聘,职业生涯规划)...

    本文是"松结对编程"系列的第六篇.(之一,之二,之三,之四,之五,之六,之七,之八) 松结对编程是小型团队的实践,大约运行在1个师傅+1-3个徒弟的尺度上,当面临更大尺度的时候,就 ...

最新文章

  1. 程序员单身比例有多高?【2019开发者图鉴】告诉你
  2. Linux命令应用大词典-第1章 登录、退出、关机和重启
  3. arduino python firmate_processing firmata协议及数组训练
  4. 实例分享--告诉你如何使用语音和自然语言控制智能家居
  5. 查询分析器在哪里_你应该知道的3种Node.js分析器类型
  6. 函数 (四) 迭代器和生成器
  7. php循环读取mysql_PHP无限循环获取MySQL数据
  8. 移动推送消息送达常见问题与解决办法
  9. activex control test container 服务器正在运行中_Desktop Central服务器RCE漏洞在野攻击分析...
  10. MySQL教程(二)—— 关于在ACCESS中使用SQL语句
  11. ~~试除法判定质数 (附模板题)
  12. 按钮 java_按钮大小(Java)
  13. Nginx 配置虚拟主机
  14. jQuery.unique引发一个血案
  15. Linux内核开发_3_busybox
  16. 修复Windows10系统的注册表?
  17. [轻松拿offer]Web前端面试真题|带详解!
  18. 【DCANet2022】:DCANet: Differential Convolution Attention Network for RGB-D Semantic Segmentation
  19. 余压监控系统在住宅小区的应用方案
  20. 在数据库应用系统中数据库的开发

热门文章

  1. 程序员如何防止脑疲劳:下午补充一些干果
  2. zeppelin安装配置
  3. 单元测试(01) 调试过程中遇到 Method isEmpty in android.text.TextUtils not mocked
  4. 纯js实现俄罗斯方块详解与源码
  5. java集成微信支付
  6. SparkLink星闪技术之SLB概述
  7. DP83848 跑10M能行,跑100M不行 的原因
  8. oracle异地双活距离,再谈异地双活容灾部署(6.24)
  9. jqGrid双击事件,并获取双击行的各个属性值
  10. Angular9 + Primeng手动搭建项目