typescript 组成: 模块、函数、变量、语句和表达式、注释
第一个typescript 程序:
const hello : string = "Hello World!"
console.log(hello)
首先通过tsc命令编译
tsc xxx.ts
查看结果
可同时编译多个ts
tsc xx1.ts xx2.ts ...
常用编译参数
--help 显示帮助信息
--module 载入扩展模块
--target 设置ECMA版本
--declaration 额外生成一个.d.ts扩展名的文件
--removeComments 删除文件的注释
--out 编译多个文件并合并到一个输出的文件
--sourcemap 生成一个sourcemap(.map)文件
--module nolmplictiAny 在表达式和声明上有隐含的any类型时报错
--watch 在监视模式下运行编译器。会监视输出文件,在它们改变时重新编译
typescript 保留关键字: 略
typescript 与面向对象
面向对象的两个概念:对象和类
1.对象:是类的一个实例,有状态和行为
2.类:类是一个模版,它描述一类对象的行为和状态
3.方法:方法是类的操作的实现步骤
实例:
class Site {
name():void {
console.log("Runoob")
}
}
var obj = new Site();
obj.name();
编译后生成
typescript 基础类型
包含有:任意类型、字符串、布尔类型、数组类型、元组、枚举、void、null、undefined、never
1.任意类型:声明为any的变量可赋予任意类型的值
2.数字类型:
let binaryLiteral: number = 6;
3.字符串类型
let name: string = "Runoob";
4.布尔类型
let flag: boolean = true;
5.数组类型
let arr: number[] = [1, 2];
let arr: Array<number> = [1, 2];
6.元组
let x: [string, number];
x = ['Runoob', 1]; // 运行正常
x = [1, 'Runoob']; // 报错
console.log(x[0]);
7.枚举
enum Color {Red, Green, Blue};
let c: Color = Color.Blue;
console.log(c); // 输出2
8.void
//用于标识方法返回值的类型,表示该方法没有返回值
function hello(): void {
alert("Hello Runoob");
}
9.null
表示对象值缺失,用typeof检测null返回object
10.undefined
用于初始化变量为一个未定义的值
11.never
其它类型(包括null和undefined)的子类型,代表从不会出现的值
• null和undefined:是其他任何类型(包括void)的子类型,可以赋值给其它类型,如数字类型,此时,赋值后的类型会变成null或undefined。而在typescript 中启用严格的空校验(--strictNummChecks)特性,就可以使得null和undefined只能被赋值给void或本身对应的类型,示例:
// 启用 --strictNullChecks
let x: number;
x = 1; // 运行正确
x = undefined; // 运行错误
x = null; // 运行错误
可以用|来支持多种类型
// 启用 --strictNullChecks
let x: number | null | undefined;
x = 1; // 运行正确
x = undefined; // 运行正确
x = null; // 运行正确
• never类型
代表从不会出现的值,意味着声明为never类型的变量只能被never类型所赋值,在函数中它通常表现为抛出异常或无法执行到终止点(例如无限循环),示例:
let x: never;
let y: number;
x = 123; // 错误
x = (() => {
throw new Error(''exception)
})(); // 正确
y = (() => {
throw new Error(''exception)
})(); // 正确
function error (): never {
throw new Error(message);
} //正确
function loop(): never {
while (true) {}
} //正确
类型断言
• 用途:将一个联合类型断言为其中一个类型、将一个类型断言为更加具体的子类、将任何一个类型断言为any、将any断言为一个具体的类型
1.将一个联合类型断言为其中一个类型
当typescript 不确定一个联合类型的变量到底是哪个类型的时候,我们只能访问此联合类型的所有类型中共有的属性或方法
interface Cat {
name: string;
run(): void;
}
interface Fish {
name: string;
swim(): void;
}
function getName(animal: Cat | Fish) {
return animal.name;
}
而有时候,我们确实需要在还不确定类型的时候就访问其中一个类型特有的属性或方法,比如
interface Cat {
name: string;
run(): void;
}
interface Fish {
name: string;
swim(): void;
}
function isFish(animal: Cat | Fish) {
if (typeof animal.swim == 'function') { // 报错
return true;
}
return false;
}
使用类型断言
interface Cat {
name: string;
run(): void;
}
interface Fish {
name: string;
swim(): void;
}
function isFish(animal: Cat | Fish) {
- if (typeof animal.swim == 'function') {
+ if (typeof (animal as Fish).swim == 'function') {
return true;
}
return false;
}
2.将一个类型断言为更加具体的子类
当类之间有继承关系时,类型断言也很常见:
interface ApiError extends Error {
code: number = 0;
}
interface HttpError extends Error {
statusCode: number = 200;
}
function isApiError (error: Error) {
if (typeof (error as ApiError).code === 'number') {
return true;
}
return false;
}
3.将任何一个类型断言为any
想引用一个在类型上不存在的属性或方法时,会报错
const foo: number = 1;
foo.length = 1; //报错
有时候,非常确定一段代码不会出错
window.foo = 1; //报错
上面的例子中,我们需要将window 上添加一个属性foo,但ts 编译时会报错,提示window上不存在foo属性
此时我们可民使用as any 临时将window 断言为any类型
(window as any).foo = 1;
在any类型的变量上,访问任何属性都是允许的
4.将any断言为一个具体的类型
日常的开发中,不可避免的需要处理any类型的变量,它们可能是由于第三方库未能定义好自己的类型,也有可能是历史遗留的或其他人编写的烂代码,还可能是受到ts类型系统的限制而无法精确定义类型的场景
遇到any类型的变量时,我们可以选择无视它,任由它滋生更多的any,我们也可民选择改进它,通过类型断言及时的把any断言为精确的类型
function getCacheData(key: string): any { // 历史遗留代码,返回值是any
return (window as any).cache[key];
}
interface Cat {
name: string;
run(): void;
}
const tom = getCacheData('tom') as Cat;
tom.run();
上面的例子中, 我们调用完getCacheData之后,立即将它断言为Cat类型。这样的话明确了tom的类型,后续对tom的访问时就有了代码补全,提高了代码的可维护性
• 类型断言的限制:并不是任何一个类型都可以被断言为任何另一个类型
interface Animal {
name: string;
}
interface Cat {
name: string;
run(): void;
}
let tom: Cat = {
name: 'Tom',
run: () => {
console.log('run')
}
}
let animal: Animal = tom;
上面的例子中,cat包含了Animal 中所有属性,除此之外,它还有一个额外的方法run。ts 并不关心Cat 和Animal之间定义时是什么关系,而只会看它们最终的结构有什么关系————所以它与Cat extends Animal是等价的
interface Animal {
name: string;
}
interface Cat extends Animal {
run(): void;
}
即Animal兼容Cat
当Animal兼容Cat时,它们就可相互进行类型断言了
interface Animal {
name: string;
}
interface Cat {
name: string;
run(): void;
}
function testAnimal(animal: Animal) {
return (animal as Cat);
}
function testCat(cat: Cat) {
return (cat as Animal);
}
允许 animal as Cat 是因为父类可以被断言为子类
允许 cat as Animal 是因为既然子类拥有父类的属性和方法,那么被断言为父类,获取父类的属性、调用父类的方法,就不会有任何问题
• 双重断言
既然任何类型都可以被断言为any且any可以被断言为任何类型
是不是可以使用双重断言 as any as Foo 来将任何一个类型断言为另一个类型呢
interface Cat {
run(): void;
}
interface Fish {
swim(): void;
}
function testCat(cat: Cat) {
return (cat as any as Fish);
}
上面的例子中,若直接使用cat as Fish肯定报错,但用了双重断言,就打破了类型断言的限制,可以将任何一个类型断言为任何另一个类型,尽量不要这么干!
• 类型断言vs类型声明
function getCacheData(key: string): any {
return (window as any).cache[key];
}
interface Cat {
name: string;
run(): void;
}
- const tom = getCacheData('tom') as Cat; // 类型断言
+ const tom: Cat = getCacheData('tom'); // 类型声明
tom.run();
类型声明和类型断言是非常相似的,产生的结果也几乎是一样的——tom在接下来的代码中都变成了Cat类型
区别:
interface Animal {
name: string;
}
interface Cat {
name: string;
run(): void;
}
const animal: Animal = {
name: 'tom';
};
let tom = animal as Cat;
上面的例子中,由于Animal 兼容Cat ,故可民将animal 断言为Cat 赋值给tom
但是若直接声明tom 为Cat 类型
interface Animal {
name: string;
}
interface Cat {
name: string;
run(): void;
}
const animal: Animal = {
name: 'tom';
};
- let tom = animal as Cat;
+ let tom: Cat = animal; // 报错
Animal可以看作是Cat的父类,当然不能将父类的实例赋值给类型为子类的变量
深入讲,核心区别在于
1.animal 断言为 Cat, 只需要满足Animal 兼容Cat或Cat 兼容 Animal即可
2.animal 赋值给tom,需要满足Cat 兼容Animal才行
...未完待续