???? 如果想阅读最新的文章,或者有技术问题需要交流和沟通,可搜索并关注小红书“希望睿智”。
背景
□ TypeScript起源于使用JavaScript开发的大型项目。由于JavaScript语言本身的局限性,难以胜任和维护大型项目开发,因此微软开发了TypeScript,使得其能够胜任开发大型项目。
□ TypeScript是微软开发的一个开源的编程语言,通过在JavaScript的基础上添加静态类型定义构建而成。TypeScript通过TypeScript编译器或Babel转译为JavaScript代码,可运行于任何浏览器,任何操作系统。
□ 2012年10月,微软发布了首个公开版本的TypeScript。2013年6月19日,在经历了一个预览版之后,微软发布了正式版的TypeScript。
□ TypeScript的作者是安德斯·海尔斯伯格,他也是C#的首席架构师。
特性
TypeScript是一种给JavaScript添加特性的语言扩展,是JavaScript 的一个超集。
类型批注
□ 布尔值
let isDone: boolean = false;
□ 数字
let decLiteral: number = 6;
let hexLiteral: number = 0xf00d;
let binaryLiteral: number = 0b1010;
□ 字符串
let name: string = "bob";
□ 数组
let list: number[] = [1, 2, 3];
let list: Array<number> = [1, 2, 3];
□ 元组tuple
let x: [string, number] = ['hello', 10];
□ 枚举
enum Color {Red = 1, Green = 2, Blue = 4}
let c: Color = Color.Green;
let colorName: string = Color[2];
alert(colorName);
□ 任意值
let notSure: any = 4;
notSure = true;
let list: any[] = [1, true, "free"];
list[1] = 100;
□ 空值
function func(): void {
alert("hello world");
}
// void类型的变量只能被赋值成undefined和null
let unusable: void = undefined;
□ null和undefined
let a: undefined = undefined;
let b: null = null;
默认情况下,null和undefined是所有类型的子类型,也就是说,你可以把null和undefined赋值给number类型的变量。但当你指定了--strictNullChecks标记,null和undefined只能赋值给void和它们自身,这能避免很多常见的问题。
□ 联合类型
联合类型表示一个值可以是几种类型之一。 我们用竖线(|)分隔每个类型,所以number | string | boolean表示一个值可以是number,string,或boolean。
如果一个值是联合类型,我们只能访问此联合类型的所有类型里共有的成员。
interface Bird {
fly(); layEggs();
}
interface Fish {
swim(); layEggs();
}
function getSmallPet(): Fish | Bird {}
let pet = getSmallPet();
pet.layEggs();
// 不是共有成员,会报错
pet.swim();
□ 类型转换(类型断言)
方式1:
let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length;
方式2:
let someValue: any = "this is a string";
let strLength: number = (someValue as string).length;
两种形式是等价的,至于使用哪个,大多数情况下是凭个人喜好。在TypeScript里使用JSX时,只有as语法是被允许的。
接口
□ 接口定义
interface ConfigOptions {
// 必须带的属性
color: string;
// 可选属性
width?: number;
// 只读属性
readonly x: number;
// 函数类型
setTime(d: Date);
}
interface ConfigOptions2 {
color: string;
// 字符串索引签名(可以有任意数量的其他属性)
[propName: string]: any;
}
interface StringArray {
// 数字索引签名
[index: number]: string;
}
let myArray: StringArray;
myArray = ["Bob", "Fred"];
let myStr: string = myArray[0];
□ 接口使用
function create(config: ConfigOptions) {
}
create({ colour: "red", width: 100, x: 2 });
create({ colour: "red", width: 100, x: 2 } as ConfigOptions);
□ 继承接口
interface Shape {
color: string;
}
interface Square extends Shape {
len: number;
}
类
□ 类的定义
class Greeter {
greeting: string;
// 只读属性必须在声明时或构造函数里被初始化
readonly name: string;
readonly numberOfLegs: number = 8;
constructor(message: string, name: string) {
this.greeting = message;
this.name = name;
}
greet() {
return "Hello, " + this.greeting;
}
}
□ 参数属性
参数属性通过给构造函数参数添加一个访问限定符来声明。使用private限定一个参数属性会声明并初始化一个私有成员,对于public和protected来说也是一样。
class Animal {
constructor(private name: string) { }
move(distanceInMeters: number) {
console.log(${this.name} moved ${distanceInMeters}m.);
}
}
□ 存取器
通过getters/setters来截取对对象成员的访问
class Employee {
private _fullName: string;
get fullName(): string {
return this._fullName;
}
set fullName(newName: string) {
this._fullName = newName;
}
}
let employee = new Employee();
employee.fullName = "Bob Smith";
alert(employee.fullName);
□ 类的继承
class Animal {
move(distanceInMeters: number = 0) {
console.log(Animal moved ${distanceInMeters}m.);
}
}
class Dog extends Animal {
bark() {
console.log('Woof! Woof!');
}
}
let dog = new Dog();
dog.bark();
dog.move(10);
□ 抽象类
abstract class Department {
constructor(public name: string) {
}
abstract printMeeting(): void; // 必须在派生类中实现
}
class AccountingDepartment extends Department {
constructor() {
super('Accounting and Auditing');
}
printMeeting(): void {
console.log('The Accounting Department meets each Monday at 10am.');
}
}
模块
□ 从ECMAScript 2015开始,JavaScript引入了模块的概念,TypeScript也沿用了这个概念。
□ 模块在其自身的作用域里执行,而不是在全局作用域里。这意味着定义在一个模块里的变量、函数、类等在模块外部是不可见的,除非你明确地使用export导出它们。相反,如果想使用其它模块导出的变量、函数、类、接口等的时候,你必须要导入它们,可以使用import。
□ 模块是自声明的,两个模块之间的关系是通过在文件级别上使用import和export建立的。
export
export as
export default
import
import as
import default
泛型
□ 设计一个函数echo,这个函数会返回任何传入它的值。
function echo(arg: number): number {
return arg;
}
function echo(arg: any): any {
return arg;
}
function echo<T>(arg: T): T {
return arg;
}
let output = echo<string>("myString");
let output = echo("myString");
□ 使用泛型创建泛型函数时,编译器要求你在函数体内必须正确使用这个通用的类型。换句话说,你必须把这些参数当作是任意或所有类型。
function echo<T>(arg: T): T {
console.log(arg.length);
return arg;
}
function echo<T>(arg: T[]): T[] {
console.log(arg.length);
return arg;
}
let myEcho: <T>(arg: T) => T = echo;
□ 泛型类
class GenericNumber<T> {
zeroValue: T;
add: (x: T, y: T) => T;
}
let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
let stringNumeric = new GenericNumber<string>();
stringNumeric.zeroValue = "hello";
□ 泛型约束
interface Lengthwise {
length: number;
}
function echo<T extends Lengthwise>(arg: T): T {
console.log(arg.length);
return arg;
}
// 约束不通过
echo(3);
// 约束通过
echo({length: 10, value: 3});
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");
// 约束不通过
getProperty(x, "m");
□ 类类型(使用泛型创建工厂函数时,需要引用构造函数的类类型)
class Animal {
numLegs: number;
}
class Bee extends Animal {
name1: string;
}
class Lion extends Animal {
name2: string;
}
function createInstance<A extends Animal>(c: new () => A): A {
return new c();
}
alert(createInstance(Lion).name2)