var name: string = Gene;
var age: number = 37;
var sentence: string = `Hello, my name is ${ name }.
var list: any[] = [1, true, “free”];
下面我们重写上面的例子,这次使用接口来描述:必须包含一个label属性且类型为string:
还有一点值得提的是,类型检查器不会去检查属性的顺序,只要相应的属性存在并且类型也是对的就可以。
下面是应用了“option bags”的例子:
一个例子就是,一个对象可以同时做为函数和对象使用,并带有额外的属性。
看下面的例子:
下面来看一个例子,详细的解释了这点:
constructor(name: string, department: string) { super(name) this.department = department; } public getElevatorPitch() { return `Hello, my name is ${this.name} and I work in ${this.department}.`; }
}
var passcode = “secret passcode”;
get fullName(): string { return this._fullName; } set fullName(newName: string) { if (passcode && passcode == "secret passcode") { this._fullName = newName; } else { console.log("Error: Unauthorized update of employee!"); } }
}
注意:若要使用存取器,要求设置编译器输出目标为ECMAScript 5或更高。
让我们来改写一下这个例子,看看它们之前的区别:
但就像其它全局命名空间污染一样,这很难去了解组件之间的依赖关系,尤其是在大型的应用中。
对于Node.js应用来说,模块是默认的并组是推荐的组织代码的方式。
从ECMAScript 2015开始,模块成为了语言内置的部分,应该会被所有正常的解释引擎所支持。
对于新的项目来说模块应该是首选组织代码的形式。
其次,与前一步相似,去查找.d.ts文件,不同的是它不是具体实现文件而是声明文件(同样具有顶级的import或export声明)。
最后,是查找“外部模块的声明”,它是通过declare和使用被引号括住的名字定义的。
下面是改进的例子:
就像我们在术语说明里提到的那样,“内部模块”现在叫做“命名空间”。
另外,任何使用module关键字来声明一个内部模块的地方都应该使用namespace关键字来替换。
这就避免了让新的使用者被相似的名称所迷惑。
var lettersRegexp = /1+ / ; v a r n u m b e r R e g e x p = / [ 0 − 9 ] + /; var numberRegexp = /^[0-9]+ /;varnumberRegexp=/[0−9]+/;
const lettersRegexp = /^[A-Za-z]+$/; const numberRegexp = /^[0-9]+$/; export class LettersOnlyValidator implements StringValidator { isAcceptable(s: string) { return lettersRegexp.test(s); } } export class ZipCodeValidator implements StringValidator { isAcceptable(s: string) { return s.length === 5 && numberRegexp.test(s); } }
}
第一种方式,把所有的输入文件编译为一个输出文件,需要使用–out标记:
export interface Event { x: number; y: number; } export interface Base extends Selectors { event: Event; }
}
模块是自声明的;两个模块之间的关系是通过在文件级别上使用imports和exports建立的。
TypeScript与CMAScript 2015一样,任何包含顶级import或者export的文件都被当成一个模块。
var strings = [“Hello”, “98052”, “101”];
若要导入一个使用了export =的模块时,必须使用TypeScript提供的特定语法import var = require(“module”)。
下面的例子说明了导入导出语句里使用的名字是怎么转换为相应的模块加载器代码的。
const lettersRegexp = /7+$/;
const numberRegexp = /8+$/;
import { ZipCodeValidator as Zip } from “./ZipCodeValidator”;
import { ZipCodeValidator as Zip } from “./ZipCodeValidator”;
import { ZipCodeValidator as Zip } from “./ZipCodeValidator”;
export function parse(urlStr: string, parseQueryString?, slashesDenoteHost?): Url;
}
protected processDigit(digit: string, currentValue: number) { if (digit >= "0" && digit <= "9") { return currentValue * 10 + (digit.charCodeAt(0) - "0".charCodeAt(0)); } } protected processOperator(operator: string) { if (["+", "-", "*", "/"].indexOf(operator) >= 0) { return operator; } } protected evaluateOperator(operator: string, left: number, right: number): number { switch (this.operator) { case "+": return left + right; case "-": return left - right; case "*": return left * right; case "/": return left / right; } } private evaluate() { if (this.operator) { this.memory = this.evaluateOperator(this.operator, this.memory, this.current); } else { this.memory = this.current; } this.current = 0; } public handelChar(char: string) { if (char === "=") { this.evaluate(); return; } else { let value = this.processDigit(char, this.current); if (value !== undefined) { this.current = value; return; } else { let value = this.processOperator(char); if (value !== undefined) { this.evaluate(); this.operator = value; return; } } } throw new Error(`Unsupported input: '${char}'`); } public getResult() { return this.memory; }
}
console.log(`result of '${input}' is '${c.getResult()}'`);
ProgrammerCalculator.ts
import { Calculator } from “./Calculator”;
constructor(public base: number) { super(); if (base <= 0 || base > ProgrammerCalculator.digits.length) { throw new Error("base has to be within 0 to 16 inclusive."); } } protected processDigit(digit: string, currentValue: number) { if (ProgrammerCalculator.digits.indexOf(digit) >= 0) { return currentValue * this.base + ProgrammerCalculator.digits.indexOf(digit); } }
}
更多关于模块和命名空间的资料查看[命名空间和模块](./Namespaces and Modules.md)
通过下面的例子可以迅速回想起这两种JavaScript中的函数:
var z = 100;
// The parameters x and y have the type number
var myAdd: (baseValue:number, increment:number)=>number =
function(x, y) { return x+y; };
1
2
3
4
5
6
这叫做‘按上下文归类’,是类型推论的一种。
它帮助我们更好地为程序指定类型。
可选参数与默认值参数共享参数类型。
在TypeScript里,你可以把所有参数收集到一个变量里:
这个省略号也会在带有剩余参数的函数类型定义上使用到:
下面看一个例子:
return {suit: this.suits[pickedSuit], card: pickedCard % 13}; }
return {suit: this.suits[pickedSuit], card: pickedCard % 13}; }
var suits = [“hearts”, “spades”, “clubs”, “diamonds”];
var suits = [“hearts”, “spades”, “clubs”, “diamonds”];
不用泛型的话,这个函数可能是下面这样:
第二种方法更普遍。利用了类型推论,编译器会根据传入的参数自动地帮助我们确定T的类型:
看下之前identity例子:
我们也可以这样实现上面的例子:
泛型函数的类型与非泛型函数的类型没什么不同,只是有一个类型参数在最前面,像函数声明一样:
var myIdentity: (arg: U) => U = identity;
1
2
3
4
5
我们还可以使用带有调用签名的对象字面量来定义泛型函数:
function find
(n: T, s: U) { // errors because type parameter used in constraint
// …
}
find (giraffe, myAnimals);
1
2
3
4
可以通过下面的方法来实现,重写上面的代码,
function findKeeper (a: {new(): A;
prototype: {keeper: K}}): K {
}
}
首先,在了解如何进行声明合并之前,让我们先看一下什么叫做声明合并。
理解了每种声明会对应创建什么对于理解如果进行声明合并是有帮助的。
合并接口
接口中非函数的成员必须是唯一的。如果多个接口中具有相同名字的非函数成员就会报错。
对于函数成员,每个同名函数声明都会被当成这个函数的一个重载。
需要注意的是,接口A与它后面的接口A(把这个接口叫做A’)合并时,A’中的重载函数具有更高的优先级。
如下例所示:
合并命名空间
命名空间的合并,模块导出的同名接口进行合并,构成单一命名空间内含合并后的接口。
值的合并,如果当前已经存在给定名字的命名空间,那么后来的命名空间的导出成员会被加到已经存在的那个模块里。
Animals声明合并示例:
等同于:
下例提供了更清晰的说明:
命名空间与类和函数和枚举类型合并
相似的,命名空间可以用来扩展枚举型:
非法的合并
类型推论 介绍
这节介绍TypeScript里的类型推论。即,类型是在哪里如何被推断的。
基础
TypeScript里,在有些没有明确指出类型的地方,类型推论会帮助提供类型。如下面的例子
最佳通用类型
当需要从几个表达式中推断类型时候,会使用这些表达式的类型来推断出一个最合适的通用类型。例如,
由于最终的通用类型取自候选类型,有些时候候选类型共享相同的通用类型,但是却没有一个类型能做为所有候选类型的类型。例如:
上下文类型
类型兼容 介绍
在使用名义类型的语言,比如C#或Java中,这段代码会报错,因为Person类没有明确说明其实现了Named接口。
关于可靠性的注意事项
TypeScript的类型系统允许一些在编译阶段无法否认其安全性的操作。当一个类型系统具此属性时,被当做是“不可靠”的。TypeScript允许这种不可靠行为的发生是经过仔细考虑的。通过这篇文章,我们会解释什么时候会发生这种情况和其有利的一面。
开始
TypeScript结构化类型系统的基本规则是,如果x要兼容y,那么y至少具有与x相同的属性。比如:
检查函数参数时使用相同的规则:
这个比较过程是递归进行的,检查每个成员及子成员。
比较两个函数
第二个赋值错误,因为y有个必需的第二个参数,但是x并没有,所以不允许赋值。
下面来看看如何处理返回值类型,创建两个仅是返回值类型不同的函数:
类型系统强制源函数的返回值类型必须是目标函数返回值类型的子类型。
函数参数双向协变
可选参数及剩余参数
当一个函数有剩余参数时,它被当做无限个可选参数。
这对于类型系统来说是不稳定的,但从运行时的角度来看,可选参数一般来说是不强制的,因为对于大多数函数来说相当于传递了一些undefinded。
有一个好的例子,常见的函数接收一个回调函数并用对于程序员来说是可预知的参数但对类型系统来说是不确定的参数来调用:
函数重载
枚举
枚举类型与数字类型兼容,并且数字类型与枚举类型兼容。不同枚举类型之间是不兼容的。比如,
类
类的私有成员
泛型
因为TypeScript是结构性的类型系统,类型参数只影响使用其做为类型一部分的结果类型。比如,
在这里,泛型类型在使用时就好比不是一个泛型类型。
比如,
高级主题
子类型与赋值
.d.ts文件 介绍
指导与说明
流程
命名空间
回调函数
扩展与声明合并
匿名类型var
接口类型var
从使用者角度来讲,它们是相同的,但是SomePoint类型能够通过接口合并来扩展:
类的分解
你可以使用typeof关键字来拿到类静态部分类型,在写声明文件时,想要把类明确的分解成实例类型和静态类型时是有用且必要的。
下面是一个例子,从使用者的角度来看,这两个声明是等同的:
标准版
分解版
这里的利弊如下:
- 标准方式可以使用extends来继承;分解的类不能。这可能会在未来版本的TypeScript里改变:是否允许任何的extends表达式
- 都允许之后为类添加静态成员
- 允许为分解的类再添加实例成员,标准版不允许
- 使用分解类的时候,为成员起合理的名字
命名规则
例子
参数对象
使用方法
类型
带属性的函数
使用方法
类型
可以用new调用也可以直接调用的方法
使用方法
类型
全局的/不清楚的Libraries
使用方法
类型
外部模块的单个复杂对象
使用方法
类型
回调函数
使用方法
类型
发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/219858.html原文链接:https://javaforall.net
