前言
JavaScript类型约束规范(typescript),TypeScript是JavaScript的超集。
安装
npm install -g typescript
编译
tsc helloword.ts
基础类型
let isDone: boolean = false; // 布尔值 true false
let decLiteral: number = 6; // 数字 6 0xf00d 0b1010 0o744
let name: string = "bob"; // 字符串 双引号、单引号、模板字符串
let list1: number[] = [1, 2, 3]; // 数组
let list2: Array<number> = [1, 2, 3]; // 数组泛型
let x: [string, number],x = ['hello', 10]; // 元素 Tuple
enum Color {Red, Green, Blue} // 枚举 默认从0开始为元素编号,可以自定义如 {Red=1}
let c: Color = Color.Green;
let notSure: any = 4; // 任意值,可为任意类型
let unusable: void = undefined; // 空值 undefined null,或函数没有返回值的返回值类型
let u: undefined = undefined; // typescript中undefined类型
let n: null = null; // typescript中null类型
function error(message: string): never { // never类型表示的是那些永不存在的值的类型
throw new Error(message);
}
类型断言
// 类型断言跳过编译阶段类型检查
let value: any = "It is a string",strLength: number = (<string>value).length;
let value: any = "It is a string",strLength: number = (value as string).length;
变量声明
var、let、const变量声明方式,var 声明全局变量,let 声明局部变量,const 声明常量(不能被修改,若是引用类型,存的是引用地址,引用的对象属性可以修改)。
let、const 具有块级作用域,不能在被声明之前读或写。
解构
let input = [1, 2];
let [first, second] = input;
[first, second] = [second, first];
let [first, ...rest] = [1, 2, 3, 4];
let [, second, , fourth] = [1, 2, 3, 4]; // 逗号占位
let obj = {
a: "foo",
b: 12,
c: "bar"
};
let { a, b } = obj;
let { a: newName1, b: newName2 } = obj; // 属性重命名
let {a, b}: {a: string, b: number} = obj; // 指定类型
type C = { a: string, b?: number };
function f({ a, b }: C): void {};
function f({ a, b } = { a: "", b: 0 }): void {};
let first = [1, 2];
let second = [3, 4];
let bothPlus = [0, ...first, ...second, 5]; // 展开,若是对象,属性相同会重写,
let defaults = { food: "spicy", price: "$$", ambiance: "noisy" };
let search = { food: "rich", ...defaults };
接口
function printLabel(labelledObj: { label: string }) {
console.log(labelledObj.label);
}
let myObj = { size: 10, label: "Size 10 Object" };
printLabel(myObj);
interface SquareConfig {
color?: string;
width?: number;
}
interface Point {
readonly x: number;
readonly y: number;
[propName: string]: any; // 带有任意数量的其它属性
}
// 函数类型
interface SearchFunc {
(source: string, subString: string): boolean;
}
let mySearch: SearchFunc = (source: string, subString: string) => {
let result = source.search(subString);
return result > -1;
}
interface StringArray {
[index: number]: string;
}
let myArray: StringArray = ["Bob", "Fred"];
let myStr: string = myArray[0];
interface ReadonlyStringArray {
readonly [index: number]: string;
}
// 实现接口
interface ClockInterface {
currentTime: Date;
setTime(d: Date);
}
class Clock implements ClockInterface {
currentTime: Date;
setTime(d: Date) {
this.currentTime = d;
}
constructor(h: number, m: number) { }
}
// 继承接口
interface Shape {
color: string;
}
interface Square extends Shape {
sideLength: number;
}
let square = <Square>{};
square.color = "blue";
square.sideLength = 10;
// 混合类型
interface Counter {
(start: number): string;
interval: number;
reset(): void;
}
function getCounter(): Counter {
let counter = <Counter>function (start: number) { };
counter.interval = 123;
counter.reset = function () { };
return counter;
}
let c = getCounter();
c(10);
c.reset();
c.interval = 5.0;
// 接口继承类
class Control {
private state: any;
}
interface SelectableControl extends Control {
select(): void;
}
class Button extends Control implements SelectableControl {
select() { }
}
class TextBox extends Control {
}
// Error: Property 'state' is missing in type 'Image'.
class Image implements SelectableControl {
select() { }
}
class Location {
}
类
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
return "Hello, " + this.greeting;
}
}
let greeter = new Greeter("world");
class Animal {
static origin = "ORIGIN"; // 静态属性存在于类本身,通过【类名.静态属性】访问
private name: string; // 默认public,private protected,readonly
constructor(name: string) { this.name = name; }
get fullName(): string {
return this.name;
}
set fullName(newName: string) {
this.name = newName;
}
move(distanceInMeters: number = 0) {
console.log(`Animal moved ${distanceInMeters}m.`);
}
}
class Dog extends Animal {
constructor(name: string) { super(name); }
bark() {
console.log('Woof! Woof!');
}
}
let dog: Dog;
dog = new Dog();
dog.bark();
dog.move(10);
dog.bark();
// 抽象类
abstract class Animal { // 抽象类做为其它派生类的基类使用,抽象方法不包含具体实现并且必须在派生类中实现
abstract makeSound(): void;
move(): void {
console.log('roaming the earch...');
}
}
let Greeter = (function () {
function Greeter(message) {
this.greeting = message;
}
Greeter.prototype.greet = function () {
return "Hello, " + this.greeting;
};
return Greeter;
})();
let greeter;
greeter = new Greeter("world");
console.log(greeter.greet());
class Greeter {
static standardGreeting = "Hello, there";
greeting: string;
greet() {
if (this.greeting) {
return "Hello, " + this.greeting;
}
else {
return Greeter.standardGreeting;
}
}
}
let greeter1: Greeter;
greeter1 = new Greeter();
console.log(greeter1.greet());
let greeterMaker: typeof Greeter = Greeter;
greeterMaker.standardGreeting = "Hey there!";
let greeter2: Greeter = new greeterMaker();
console.log(greeter2.greet());
// 类和接口
class Point {
x: number;
y: number;
}
interface Point3d extends Point {
z: number;
}
let point3d: Point3d = {x: 1, y: 2, z: 3};
函数
let myAdd: (x:number, y:number, z?:number) => number;
myAdd = (x: number, y=0, z?:number): number => { return x + y + z; };
function buildName(firstName: string, ...restOfName: string[]) {
return firstName + " " + restOfName.join(" ");
}
let buildNameFun: (fname: string, ...rest: string[]) => string = buildName;
泛型
用于表示类型而不是值
function identity<T>(arg: T): T {
return arg;
}
let output = identity<string>("myString");
let output = identity("myString"); // 类型推论 – 即编译器会根据传入的参数自动地帮助我们确定T的类型
let myIdentity: <T>(arg: T) => T = identity;
// 泛型接口
interface GenericIdentityFn {
<T>(arg: T): T;
}
function identity<T>(arg: T): T {
return arg;
}
let myIdentity: GenericIdentityFn = identity;
// 泛型类
class GenericNumber<T> {
zeroValue: T;
add: (x: T, y: T) => T;
}
let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) { return x + y; };
// 泛型约束
interface Lengthwise {
length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T { // 这个泛型函数被定义了约束,因此它不再是适用于任意类型
console.log(arg.length); // Now we know it has a .length property, so no more error
return arg;
}
// 在泛型约束中使用类型参数,eg:下面这两个类型之间使用约束
function getProperty<T, K extends keyof T>(obj: T, key: K) {
return obj[key];
}
let x = { a: 1, b: 2, c: 3, d: 4 };
getProperty(x, "a"); // okay
getProperty(x, "m"); // error: Argument of type 'm' isn't assignable to 'a' | 'b' | 'c' | 'd'.
// 在泛型约束中使用类型参数
function create<T>(c: {new(): T; }): T {
return new c();
}
class BeeKeeper {
hasMask: boolean;
}
class ZooKeeper {
nametag: string;
}
class Animal {
numLegs: number;
}
class Bee extends Animal {
keeper: BeeKeeper;
}
class Lion extends Animal {
keeper: ZooKeeper;
}
function createInstance<A extends Animal>(c: new () => A): A {
return new c();
}
createInstance(Lion).keeper.nametag;
createInstance(Bee).keeper.hasMask;
枚举
enum Direction {
Up = 1,
Down,
Left,
Right
}
enum FileAccess {
// constant members
None,
Read = 1 << 1,
Write = 1 << 2,
ReadWrite = Read | Write,
// computed member
G = "123".length
}
// 外部枚举
declare enum Enum {
A = 1,
B,
C = 2
}
类型推论
let x = 3; // 推断数字类型
let x = [0, 1, null]; // x:(number|null)[]
let zoo: Animal[] = [new Rhino(), new Elephant(), new Snake()]; // 最佳通用类型 Animal[]
let zoo: (Rhino | Elephant | Snake)[] = = [new Rhino(), new Elephant(), new Snake()]; // 联合数组类型
// 类型别名
type Name = string;
type NameResolver = () => string;
type NameOrResolver = Name | NameResolver;
function getName(n: NameOrResolver): Name {
if (typeof n === 'string') {
return n;
}
else {
return n();
}
}
type Container<T> = { value: T };
type Tree<T> = {
value: T;
left: Tree<T>;
right: Tree<T>;
}
type LinkedList<T> = T & { next: LinkedList<T> };
// 类型别名和接口
type Alias = { num: number }
interface Interface {
num: number;
}
declare function aliased(arg: Alias): Alias;
declare function interfaced(arg: Interface): Interface;
// 字符串字面量类型
type Easing = "ease-in" | "ease-out" | "ease-in-out";
class UIElement {
animate(dx: number, dy: number, easing: Easing) {
if (easing === "ease-in") {
// ...
}
else if (easing === "ease-out") {
}
else if (easing === "ease-in-out") {
}
else {
// error! should not pass null or undefined.
}
}
}
Symbols
// symbol类型的值是通过Symbol构造函数创建的,是不可改变且唯一的,可以被用做对象属性的键。
let sym1 = Symbol();
let sym2 = Symbol("key"); // 可选的字符串key
迭代器
当一个对象实现了Symbol.iterator属性时,我们认为它是可迭代的。 一些内置的类型如Array,Map,Set,String,Int32Array,Uint32Array等都已经实现了各自的Symbol.iterator。 对象上的Symbol.iterator函数负责返回供迭代的值
// for..of会遍历可迭代的对象,调用对象上的Symbol.iterator方法。for..of和for..in均可迭代一
// 个列表;但是用于迭代的值却不同,for..in迭代的是对象的键的列表,而for..of则迭代对象的键对应
// 的值。
let list = [4, 5, 6];
for (let i in list) {
console.log(i); // "0", "1", "2",
}
for (let i of list) {
console.log(i); // "4", "5", "6"
}
模块
介绍:从ECMAScript 2015开始,JavaScript引入了模块的概念。TypeScript也沿用这个概念。模块在其自身的作用域里执行,而不是在全局作用域里;这意味着定义在一个模块里的变量,函数,类等等在模块外部是不可见的,除非你明确地使用export形式之一导出它们。 相反,如果想使用其它模块导出的变量,函数,类,接口等的时候,你必须要导入它们,可以使用import形式之一。模块是自声明的;两个模块之间的关系是通过在文件级别上使用imports和exports建立的。
模块使用模块加载器去导入其它的模块。 在运行时,模块加载器的作用是在执行此模块代码前去查找并执行这个模块的所有依赖。 大家最熟知的JavaScript模块加载器是服务于Node.js的CommonJS和服务于Web应用的RequireJS。TypeScript与ECMAScript 2015一样,任何包含顶级import或者export的文件都被当成一个模块。
// 导出
export interface StringValidator {
isAcceptable(s: string): boolean;
}
export { ZipCodeValidator };
export { ZipCodeValidator as mainValidator };
export * from "module"; // 一个模块可以包裹多个模块,并把他们导出的内容联合在一起通过语法
// 导入
import { ZipCodeValidator } from "./ZipCodeValidator";
import { ZipCodeValidator as ZCV } from "./ZipCodeValidator";
import * as validator from "./ZipCodeValidator"; // 将整个模块导入到一个变量,并通过它来访问模块的导出部分
import "./my-module.js"; // 这些模块可能没有任何的导出。 使用该语法来导入这类模块。
// 一个模块只能够有一个default导出。
CommonJS和AMD都有一个exports对象的概念,它包含了一个模块的所有导出内容。
它们也支持把exports替换为一个自定义对象。 默认导出就好比这样一个功能;然而,它们却并不相互兼容。 TypeScript模块支持export =语法以支持传统的CommonJS和AMD的工作流模型。
export =语法定义一个模块的导出对象。 它可以是类,接口,命名空间,函数或枚举。
若要导入一个使用了export =的模块时,必须使用TypeScript提供的特定语法import module = require("module")。
let numberRegexp = /^[0-9]+$/;
class ZipCodeValidator {
isAcceptable(s: string) {
return s.length === 5 && numberRegexp.test(s);
}
}
export = ZipCodeValidator; // 导出
import zip = require("./ZipCodeValidator"); // 导入
生成模块代码
(CommonJS),Require.js (AMD),isomorphic (UMD), SystemJS或ECMAScript 2015 native modules (ES6)模块加载系统使用的代码。 想要了解生成代码中define,require 和 register的意义,请参考相应模块加载器的文档。
tsc --module commonjs Test.ts // 生成commonjs代码
编译器会检测是否每个模块都会在生成的JavaScript中用到。 如果一个模块标识符只在类型注解部分使用,并且完全没有在表达式中使用时,就不会生成require这个模块的代码。 省略掉没有用到的引用对性能提升是很有益的,并同时提供了选择性加载模块的能力。
使用其它的JavaScript库
想描述非TypeScript编写的类库的类型,需要声明类库所暴露出的API。叫它声明因为它不是“外部程序”的具体实现。 它们通常是在.d.ts文件里定义的。
外部模块
// 外部模块
可以使用顶级的export声明来为每个模块都定义一个.d.ts文件,但最好还是写在一个大的.d.ts文件里。我
们使用与构造一个外部命名空间相似的方法,但是这里使用module关键字并且把名字用引号括起来,方便之后
import。 例如:
node.d.ts (simplified excerpt)
declare module "url" {
export interface Url{
protocol?:string;
hostname?:string;
pathname?:string;
}
export function parse(urlStr:string,parseQueryString?,slashesDenoteHost?):Url;
}
declare module "path" {
export function normalize(p:string):string;
export function join(...paths:any[]):string;
export let sep:string;
}
现在我们可以/// <reference> node.d.ts并且使用import url = require("url");或import * as URL from "url"加载模块。
/// <reference path="node.d.ts"/>
import * as URL from "url";
let myUrl = URL.parse("http://www.typescriptlang.org");
外部模块简写
假如你不想在使用一个新模块之前花时间去编写声明,你可以采用声明的简写形式以便能够快速使用它。
declarations.d.ts
declare module "hot-new-module";
简写模块里所有导出的类型将是any。
importx,{y}from"hot-new-module";x(y);
模块声明通配符
某些模块加载器如SystemJS 和AMD支持导入非JavaScript内容。 它们通常会使用一个前缀或后缀来表示特殊的加载语法。 模块声明通配符可以用来表示这些情况。
declare module "*!text" {
const content: string;
export default content;
}
// Some do it the other way around.
declare module "json!*" {
const value: any;
export default value;
}
现在你可以就导入匹配"*!text"或"json!*"的内容了。
import fileContent from "./xyz.txt!text";
import data from "json!http://example.com/data.json";
console.log(data, fileContent);
命名空间
命名空间和模块
模块解析
声明合并
书写.d.ts文件
注意
1.Object类型的变量只允许你给它赋任意值,但是却不能够在它上面调用任意的方法,会报错。
2.默认情况下null和undefined是所有类型的子类型。
3.never类型是任何类型的子类型,可以赋值给任何类型;然而,没有类型是never的子类型或可以赋值给never类型(除了never本身之外)。 即使any也不可以赋值给never。
4.对象展开对象自身可枚举属性。
5.推断类型,在赋值语句的一边指定了类型但是另一边没有类型的话,TypeScript编译器会自动识别出类型。
6.箭头函数能保存函数创建时的this值,而不是调用时的值。
7.typeof关键字,当在表示类型的地方使用时,会得出一个类型值。