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”的值的类型。
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);
}
基本类型为Number,Boolean,String,Symbol,Void,Null和Undefined类型以及所有用户定义的枚举类型。
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
布尔基元类型对应于类似命名的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
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
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
由void关键字引用的Void类型表示不存在值,并用作没有返回值的函数的返回类型。
Void类型的唯一可能值为null和undefined。 Void类型是Any类型的子类型,是Null和Undefined类型的超类型,但是Void与其他所有类型都不相关。
注意:我们可能会考虑禁止声明Void类型的变量,因为它们没有用处。 但是,由于允许将Void作为泛型类型或函数的类型参数,因此不允许Void属性或参数是不可行的。
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
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
枚举类型是Number原语类型的不同用户定义的子类型。 枚举类型使用枚举声明(第9.1节)声明,并使用类型引用(第3.8.2节)进行引用。
枚举类型可分配给Number原语类型,反之亦然,但不同的枚举类型不能相互分配。
专用签名(第3.9.2.4节)允许将字符串文字用作参数类型注释中的类型。 字符串文字类型仅在该上下文中被允许,在其他地方则不允许。
所有字符串文字类型都是String基本类型的子类型。
TODO:扩展对字符串文字类型的支持。
对象类型由属性,调用签名,构造签名和索引签名(统称为成员)组成。
类和接口类型的引用,数组类型,元组类型,函数类型和构造函数类型都归为对象类型。 TypeScript语言中的多个构造可创建对象类型,包括:
对类和接口类型的类型引用(第3.8.2节)被归类为对象类型。 泛型类和接口类型的类型引用包括类型参数,这些类型参数将替换类或接口的类型参数以产生实际的对象类型。
数组类型表示具有常见元素类型的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节),则称其为类似数组的类型。
元组类型表示具有单独跟踪的元素类型的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.8.8节)或通过在对象类型文字中包含调用签名来编写函数类型。
包含一个或多个构造签名的对象类型被称为构造函数类型。 构造函数类型可以使用构造函数类型文字(第3.8.9节)或在对象类型文字中包含构造签名来编写。
每个对象类型都由零个或多个以下类型的成员组成:
属性是公共的,私有的或受保护的,并且是必需的或可选的:
通过包含带有字符串文字类型的参数,可以对调用和构造签名进行专门化(第3.9.2.4节)。专用签名用于表示模式,其中某些参数的特定字符串值导致其他参数的类型或函数结果变得更加专用。
联合类型表示可能具有几种不同表示形式之一的值。 联合类型A |的值。 B是A类型或B类型的值。联合类型使用联合类型文字(第3.8.6节)编写。
联合类型包含一组有序的组成类型。 虽然通常是A | B等于B | 答:确定联合类型的调用和构造签名时,构成类型的顺序可能很重要。
联合类型具有以下子类型关系:
同样,联合类型具有以下可分配性关系:
|| 条件运算符(第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节中定义的明显成员,不同的是,特定成员仅需要以一种或多种构成类型而不是所有构成类型存在。
交叉点类型表示同时具有多种类型的值。相交类型A和B的值是类型A和类型B的值。相交类型使用相交类型文字(第3.8.7节)编写。
交集类型包含一组有序的组成类型。虽然A和B等效于B&A通常是正确的,但是在确定调用和构造交集类型的签名时,构成类型的顺序可能很重要。
交叉点类型具有以下子类型关系:
同样,交叉点类型具有以下可分配性关系:
出于属性访问和函数调用的目的,交集类型的表观成员(第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 B
interface X { p: A }
interface Y { p: B }
var xy: X & Y = { p: ab }; // X & Y has property p of type A & B
type 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之外的其他值均不可能。 例如,基本类型(例如字符串和数字)的交集属于此类别。
类型参数表示在通用类型引用或通用函数调用中参数绑定到的实际类型。 类型参数具有约束条件,可为其实际类型参数建立上限。
由于类型参数表示大量不同的类型参数,因此与其他类型相比,类型参数具有某些限制。 特别是,类型参数不能用作基类或接口。
类,接口,类型别名和函数声明可以选择包含在<和>括号中的类型参数列表。对象,函数和构造函数类型文字的调用签名中还允许使用类型参数。
TypeParameters:
TypeParameterList:
TypeParameter
TypeParameterList,TypeParameter
TypeParameter:
BindingIdentifier Constraint(opt)
Constraint:
extends Type
类型参数名称必须唯一。如果同一TypeParameterList中的两个或多个类型参数具有相同的名称,则会发生编译时错误。
类型参数的范围扩展到与类型参数列表关联的整个声明,但类中的静态成员声明除外。
类型参数可能具有关联的类型参数约束,该约束为类型实参建立上限。可以在同一类型参数列表内的类型参数约束中引用类型参数,甚至包括出现在类型参数左侧的约束声明。
类型参数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.8.2节)必须包括用尖括号括起来并用逗号分隔的类型参数列表。 类似地,对泛型函数的调用(第4.15节)可以显式包括类型实参列表,而不是依赖于类型推断。
TypeArguments:
< TypeArgumentList >
TypeArgumentList:
TypeArgument
TypeArgumentList , TypeArgument
TypeArgument:
Type
类型参数与所引用的泛型类型或函数的类型参数一一对应。 需要一个类型实参列表为每个对应的类型形参精确指定一个类型实参,并且需要一个约束类型形参的每个类型实参来满足该类型形参的约束。 如果将类型实参替换为类型实参,则该类型实参满足类型参数约束,则该类型实参可分配给约束类型(第3.11.4节)。
给出声明
interface G<T, U extends Function> { }
类型为“ G ”的类型引用对“ A”没有任何要求,但要求“ B”可分配给“功能”。
将类型实参替换为通用类型或通用签名中的类型参数的过程称为实例化通用类型或签名。 如果提供的类型参数不满足其对应类型参数的约束,则泛型类型或签名的实例化可能会失败。
每个类和接口都有一个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的类型实参传递。
类,接口,枚举和类型别名是通过类声明(第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; }
通过引用其关键字或名称或通过编写对象类型文字,数组类型文字,元组类型文字,函数类型文字,构造函数类型文字或类型查询来指定类型。
Type:
UnionOrIntersectionOrPrimaryType
FunctionType
ConstructorType
UnionOrIntersectionOrPrimaryType:
UnionType
IntersectionOrPrimaryType
IntersectionOrPrimaryType:
IntersectionType
PrimaryType
PrimaryType:
ParenthesizedType
PredefinedType
TypeReference
ObjectType
ArrayType
TupleType
TypeQuery
ThisType
ParenthesizedType:
( Type )
当联合,交集,函数或构造函数类型用作数组元素类型时,必须在它们之间加上括号; 交集类型中的联合,函数或构造函数类型周围; 以及联合类型中的函数或构造函数类型。 例如:
(string | number)[]
((x: string) => string) | ((x: number) => number)
(A | B) & (C | D)
以下各节介绍了不同类型的类型符号。
any,number,boolean,string,symbol和void关键字分别引用Any类型和Number,Boolean,String,Symbol和Void基本类型。
PredefinedType:
any
number
boolean
string
symbol
void
预定义类型关键字是保留的,不能用作用户定义类型的名称。
类型引用通过其名称引用命名的类型或类型参数,如果是通用类型,则提供类型参数列表。
TypeReference:
TypeName [no LineTerminator here] TypeArgumentsopt
TypeName:
IdentifierReference
NamespaceName . IdentifierReference
NamespaceName:
IdentifierReference
NamespaceName . 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
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 };
};
对象类型文字通过指定静态地认为在该类型的实例中存在的成员集来定义对象类型。 可以使用接口声明为对象类型文字赋予名称,但否则是匿名的。
ObjectType:
{ TypeBodyopt }
TypeBody:
TypeMemberList ;opt
TypeMemberList ,opt
TypeMemberList:
TypeMember
TypeMemberList ; TypeMember
TypeMemberList , TypeMember
TypeMember:
PropertySignature
CallSignature
ConstructSignature
IndexSignature
MethodSignature
对象类型文字的成员被指定为属性,调用,构造,索引和方法签名的组合。 对象类型成员在第3.9节中描述。
数组类型文字被写为元素类型,后跟一个打开和关闭的方括号。
ArrayType:
PrimaryType [no LineTerminator here] [ ]
数组类型文字会引用具有给定元素类型的数组类型(第3.3.2节)。 数组类型文字是在全局命名空间中引用通用接口类型“ Array”的简写形式,其中元素类型作为类型实参。
当联合,交集,函数或构造函数类型用作数组元素类型时,必须将其括在括号中。 例如:
(string | number)[]
(() => string)[]
或者,可以使用“ Array ”符号来编写数组类型。 例如,上述类型等同于
Array<string | number>
Array<() => string>
元组类型文字是作为元素类型的序列编写的,用逗号分隔并括在方括号中。
TupleType:
[ TupleElementTypes ]
TupleElementTypes:
TupleElementType
TupleElementTypes , TupleElementType
TupleElementType:
Type
元组类型文字引用了元组类型(第3.3.3节)。
联合类型文字被写为由竖线分隔的一系列类型。
UnionType:
UnionOrIntersectionOrPrimaryType | IntersectionOrPrimaryType
联合类型字面量引用联合类型(第3.4节)。
交集类型文字被写为由&分隔的一系列类型。
IntersectionType:
IntersectionOrPrimaryType&PrimaryType
交集类型文字会引用交集类型(第3.5节)。
函数类型文字可指定调用签名的类型参数,常规参数和返回类型。
FunctionType:
TypeParametersopt(ParameterListopt)=> Type
函数类型文字是包含单个调用签名的对象类型的简写。 具体来说,是形式的函数类型文字
< T1, T2, ... > ( p1, p2, ... ) => R
与对象类型文字完全等效
{ < T1, T2, ... > ( p1, p2, ... ) : R }
请注意,具有多个调用或构造签名的函数类型不能写为函数类型文字,而必须写为对象类型文字。
构造函数类型文字可指定构造签名的类型参数,常规参数和返回类型。
ConstructorType:
new TypeParametersopt(ParameterListopt)=> Type
构造函数类型文字是包含单个构造签名的对象类型的简写。 具体来说,是以下形式的构造函数类型文字
new < T1, T2, ... > ( p1, p2, ... ) => R
与对象类型文字完全等效
{ new < T1, T2, ... > ( p1, p2, ... ) : R }
请注意,具有多个构造签名的构造函数类型不能写为构造函数类型文字,而必须写为对象类型文字。
类型查询获取表达式的类型。
TypeQuery:
typeof TypeQueryExpression
TypeQueryExpression:
IdentifierReference
TypeQueryExpression . 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()”具有相同的递归类型。
this关键字用于引用类或接口的this-type(第3.6.3节)。
ThisType:
this
ThisType的含义取决于最接近的封闭函数FunctionDeclaration,FunctionExpression,PropertyDefinition,ClassElement或TypeMember,称为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.8.3节)的成员被指定为属性,调用,构造,索引和方法签名的组合。
属性签名声明属性成员的名称和类型。
PropertySignature:
PropertyName?opt TypeAnnotationopt
TypeAnnotation:
:Type
属性签名的PropertyName(2.2.2)在其包含类型内必须是唯一的,并且如果它是计算所得的属性名称(2.2.3),则必须表示一个众所周知的符号。 如果属性名称后面带有问号,则该属性为可选。 否则,该属性是必需的。
如果属性签名省略了TypeAnnotation,则假定为Any类型。
呼叫签名定义类型参数,参数列表和返回类型,该类型参数与对包含类型的实例执行呼叫操作(第4.15节)相关。 通过定义多个不同的呼叫签名,一种类型可能会使呼叫操作超载。
CallSignature:
TypeParametersopt(ParameterListopt)TypeAnnotationopt
包含TypeParameters(第3.6.1节)的呼叫签名称为通用呼叫签名。 相反,没有TypeParameters的呼叫签名称为非通用呼叫签名。
调用签名不仅是对象类型文字的成员,还出现在方法签名(第3.9.5节),函数表达式(第4.10节)和函数声明(第6.1节)中。
包含调用签名的对象类型被称为函数类型。
调用签名中的类型参数(第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[]
签名的参数列表包含零个或多个必需参数,然后是零个或多个可选参数,最后是一个可选的rest参数。
ParameterList:
RequiredParameterList
OptionalParameterList
RestParameter
RequiredParameterList,OptionalParameterList
RequiredParameterList,RestParameter
OptionalParameterList,RestParameter
RequiredParameterList,OptionalParameterList,RestParameter
RequiredParameterList:
RequiredParameter
RequiredParameterList,RequiredParameter
RequiredParameter:
AccessibilityModifieropt BindingIdentifierOrPattern TypeAnnotationopt
BindingIdentifier:StringLiteral
AccessibilityModifier:
public
private
protected
BindingIdentifierOrPattern:
BindingIdentifier
BindingPattern
OptionalParameterList:
OptionalParameter
OptionalParameterList,OptionalParameter
OptionalParameter:
AccessibilityModifieropt BindingIdentifierOrPattern? TypeAnnotationopt
AccessibilityModifieropt BindingIdentifierOrPatternTypeTypeAnnotationoptInitializer
BindingIdentifier? : StringLiteral
RestParameter:
... BindingIdentifier TypeAnnotationopt
参数声明可以指定标识符或绑定模式(5.2.2)。参数声明中指定的标识符和参数列表中的绑定模式在该参数列表中必须是唯一的。
签名中参数的类型确定如下:
仅当参数出现在ConstructorImplementation的参数列表(第8.3.1节)中且未指定BindingPattern时,才允许其包含public,private或protected修饰符。
rest参数的类型注释必须表示数组类型。
当参数类型注释指定字符串文字类型时,包含的签名是专用签名(第3.9.2.4节)。不允许将专用签名与函数体结合使用,即FunctionExpression,FunctionImplementation,MemberFunctionImplementation和ConstructorImplementation语法生成不允许使用字符串文字类型的参数。
可以通过在参数名称或绑定模式后加上问号(?)或包含初始化程序来将参数标记为可选。仅当参数列表与函数主体一起出现时才允许使用初始化程序(包括绑定属性或元素初始化程序),即仅在FunctionExpression,FunctionImplementation,MemberFunctionImplementation或ConstructorImplementation语法生成中。
TODO:更新以反映绑定参数在实现签名中不能为可选。
TODO:更新以反映必需的参数支持初始化程序。
呼叫签名的返回类型注释(如果存在)指定由呼叫操作计算和返回的值的类型。 void返回类型注释用于指示函数没有返回值。
如果在没有函数主体的上下文中发生没有返回类型注释的调用签名,则假定返回类型为Any类型。
当在具有函数主体(特别是函数实现,成员函数实现或成员访问器声明)的上下文中发生没有返回类型注释的调用签名时,将如本节所述从函数主体中推断出返回类型。 6.3。
当参数类型注释指定字符串文字类型(第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’属性的类型包含三个专用签名,所有这些签名都可以分配给该类型中的非专用签名。
构造签名定义了与将new运算符(第4.14节)应用于包含类型的实例相关联的参数列表和返回类型。 通过定义具有不同参数列表的多个构造签名,类型可以使新操作过载。
ConstructSignature:
new TypeParametersopt(ParameterListopt)TypeAnnotationopt
构造签名的类型参数,参数列表和返回类型应遵循与调用签名相同的规则。
包含构造签名的类型被称为构造函数类型。
索引签名为包含类型中的属性定义类型约束。
IndexSignature:
[BindingIdentifier:string] TypeAnnotation
[BindingIdentifier:number] TypeAnnotation
索引签名有两种:
一个对象类型最多可以包含一个字符串索引签名和一个数字索引签名。
索引签名会影响对类型的确定,该确定是通过对包含类型的实例应用括号表示法属性访问而产生的,如第4.13节所述。
方法签名是用于声明函数类型属性的简写。
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”具有相同的类型,即具有两个调用签名的对象类型,分别带有并返回数字和字符串。
类型别名声明在包含的声明空间中引入了类型别名。
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;
};
但是,这样做意味着失去了以下功能:
TypeScript中的类型具有身份,子类型,超类型和分配兼容性关系,如以下各节所定义。
类型的表观成员是在子类型,超类型和分配兼容性关系以及属性访问(4.13节),新操作(4.14节)和函数调用(4.15节)的类型检查中观察到的成员。类型的外观成员确定如下:
如果类型不是上述之一,则认为它没有明显的成员。
实际上,类型的外观成员使其成为“对象”或“功能”接口的子类型,除非该类型定义的成员与“对象”或“功能”接口的成员不兼容(例如,如果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’的方法不兼容。
两种类型被认为是相同的
当两个成员被认为相同时
当两个调用或构造签名具有相同数量的类型参数且具有相同类型参数约束时,并且在用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”被替换后,成员将完全相同。
如果S不具有相对于T的多余属性(3.11.5),并且满足以下条件之一,则S是T类型的子类型,并且T是S的超类型。
比较调用或构造签名时,参数名称将被忽略,其余参数对应于其余参数元素类型的可选参数的无限制扩展。
注意,在确定子类型和超类型关系时,专用的调用和构造签名(第3.9.2.4节)并不重要。
另请注意,类型参数不被视为对象类型。因此,类型参数T的唯一子类型是T本身以及直接或间接约束为T的其他类型参数。
在某些情况下,类型必须与赋值兼容,例如赋值语句中的表达式和变量类型以及函数调用中的参数和参数类型。
如果S相对于T(3.11.5)没有多余的属性并且满足以下条件之一,则S可分配给T类型,T可从S分配给T:
比较调用或构造签名时,参数名称将被忽略,其余参数对应于其余参数元素类型的可选参数的无限制扩展。
注意,在确定分配兼容性时,专用的调用和构造签名(第3.9.2.4节)并不重要。
分配兼容性和子类型化规则的不同之处仅在于:
分配兼容性规则意味着,在分配值或传递参数时,可选属性必须要么存在且具有兼容类型,要么根本不存在。例如:
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
子类型和分配兼容性关系要求源类型相对于其目标类型没有多余的属性。此检查的目的是检测对象文字中过多或拼写错误的属性。
如果满足以下条件,则认为源类型S相对于目标类型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
};
在函数调用中进行类型实参推断期间(第4.15.2节),在某些情况下,有必要在参数的非泛型调用签名的上下文中实例化实参表达式的泛型调用签名,以便可以进行进一步的推断。 。 在非通用呼叫签名B的上下文中实例化通用呼叫签名A,如下所示:
在某些情况下,对给定类型参数集的推断是从一个类型S(在其中不出现这些类型参数)到另一个类型T(在其中发生这些类型参数)进行的。推论包括为每个类型参数收集的一组候选类型参数。推理过程将S和T递归关联,以收集尽可能多的推理:
比较调用或构造签名时,S中的签名按声明顺序成对地按T配对成相同类型的签名。如果S和T在给定类型的签名中具有不同的编号,则将忽略较长列表的声明顺序中多余的第一个签名。
TODO:更新以反映改进的联合和相交类型推断。
类和接口可以在其内部结构中引用自己,从而有效地创建具有无限嵌套的递归类型。 例如,类型
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
>‘类型的成员’所有者’ 类型为“列表<列表<列表<列表<列表>>>”的“所有者”,依此类推,是无限的。 由于类型关系是从结构上确定的,可能会探究组成类型的全部深度,因此,为了确定涉及无限扩展泛型类型的类型关系,编译器可能有必要在某个点终止递归,并假设不会进行进一步探索 改变结果。
在某些情况下,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[]