TypeScript
是JavaScript
的超集,继承的基础上扩展了JavaScript
,它可以实现强类型,静态类型检查的一门语言
简单来说就是因为JavaScript
是弱类型的动态类型语言, 很多错误只有在运行时才会被发现
而TypeScript
提供了一套静态检测机制, 可以帮助我们在编译时就发现错误
JavaScript
特性C,C++,Java,Go
等后端语言中的特性 (枚举、泛型、类型转换、命名空间、声明文件、类、接口等)npm i -g typescript
let num:number = 100
num = 200
tsc test1.ts
命令将ts文件转换为js文件JS的八种内置类型,七种原始类似:Boolean、Null、Undefined、Number、BigInt、String、Symbol
和 Object
let str: string = "jimmy";
let num: number = 24;
let bool: boolean = false;
let u: undefined = undefined;
let n: null = null;
let obj: object = {x: 1};
let big: bigint = 100n;
let sym: symbol = Symbol("me");
注意点:1、默认情况下 null 和 undefined 是所有类型的子类型。 就是说你可以把 null 和 undefined 赋值给其他类型。2、虽然number和bigint都表示数字,但是这两个类型不兼容。
let arr:string[] = ["1","2"];
let arr2:Array<string> = ["1","2"];
let arr:(number | string)[];
// 表示定义了一个名称叫做arr的数组,
// 这个数组中将来既可以存储数值类型的数据, 也可以存储字符串类型的数据
arr3 = [1, 'b', 2, 'c'];
// interface是接口,后面会讲到
interface ArrObj{
name:string,
age:number
}
let arr3:ArrObj[]=[{name:'jimmy', age:22}]
数组一般由同种类型的值组成,但有时我们需要在单个变量中存储不同类型的值,这时候我们就可以使用元组。在 JavaScript 中是没有元组的,元组是 TypeScript 中特有的类型,其工作方式类似于数组。
元组最重要的特性是可以限制数组元素的个数和类型,它特别适合用来实现多值返回。
let x: [string, number];
// 类型必须匹配且个数必须为2
x = ['hello', 10]; // OK
x = ['hello', 10,10]; // Error
x = [10, 'hello']; // Error
使用枚举我们可以定义一些带名字的常量。 使用枚举可以清晰地表达意图或创建一组有区别的用例。 TypeScript 支持数字的和基于字符串的枚举。
enum Direction {
NORTH,
SOUTH,
EAST,
WEST,
}
let dir: Direction = Direction.NORTH;
默认情况下,NORTH 的初始值为 0,其余的成员会从 1 开始自动增长。换句话说,Direction.SOUTH 的值为 1,Direction.EAST 的值为 2,Direction.WEST 的值为 3
TypeScript 中的接口是一个非常灵活的概念,除了可用于对类的一部分行为进行抽象以外,也常用于对**「对象的形状(Shape)」**进行描述。
interface Person {
name: string;
age: number;
}
let Semlinker: Person = {
name: "Semlinker",
age: 33,
};
// 定义的变量比接口少了一些属性是不允许的,多一些属性也是不允许的
let tom: Person = {
name: 'Tom'
};
// error TS2322: Type '{ name: string; }' is not assignable to type 'Person'.
// Property 'age' is missing in type '{ name: string; }'.
interface Person {
readonly name: string; // 只读属性。定义属性值后不能重新赋值
age?: number; // 可选属性,定义时可以不定义这个属性也不会报错
}
function sum(x: number, y: number): number {
return x + y;
}
let mySum: (x: number, y: number) => number = sum // 和上面的sum函数一样
注意点:上面的 => number
是设定函数的返回值,而不是ES6中的箭头函数中的参数
interface ISum{
(x: number, y: number): number;
}
let add2: ISum = sum
?
号,可选参数后面不能还有非可选参数function buildName(firstName: string, lastName?: string) {
if (lastName) {
return firstName + ' ' + lastName;
} else {
return firstName;
}
}
let tomcat = buildName('Tom', 'Cat');
let tom = buildName('Tom');
function buildName(firstName: string, lastName: string = 'Cat') {
return firstName + ' ' + lastName;
}
let tomcat = buildName('Tom', 'Cat');
let tom = buildName('Tom');
function push(array: any[], ...items: any[]) {
items.forEach(function(item) {
array.push(item);
});
}
let a = [];
push(a, 1, 2, 3);
在面向对象语言中,类是一种面向对象计算机编程语言的构造,是创建对象的蓝图,描述了所创建的对象共同的属性和方法。面向对象的三大特性:封装、继承、多态
类(Class):定义了一切事物的抽象特点
对象(Object):类的实例
在 TypeScript 中,我们可以通过Class
关键字来定义一个类:
class Greeter {
// 静态属性,不需要实例化也能直接调用
static cname: string = "Greeter";
// 成员属性
greeting: string;
// 构造函数 - 执行初始化操作
constructor(message: string) {
this.greeting = message;
}
// 静态方法
static getClassName() {
return "Class name is Greeter";
}
// 成员方法
greet() {
return "Hello, " + this.greeting;
}
}
let greeter = new Greeter("world");
public: 修饰的属性或方法是共有的,可以在任何地方被访问
private:修饰的属性和方法是私有的,只能被其定义所在的类访问
protected:修饰的属性或方法是受保护的,可以被其自身以及子类和父类访问
getter
和 setter
方法来实现数据的封装和有效性校验,防止出现异常数据。let passcode = "Hello TypeScript";
class Employee {
private _fullName: string;
get fullName(): string {
return this._fullName;
}
set fullName(newName: string) {
if (passcode && passcode == "Hello TypeScript") {
this._fullName = newName;
} else {
console.log("Error: Unauthorized update of employee!");
}
}
}
let employee = new Employee();
employee.fullName = "Semlinker";
if (employee.fullName) {
console.log(employee.fullName);
}
class Animal {
name: string;
constructor(theName: string) {
this.name = theName;
}
move(distanceInMeters: number = 0) {
console.log(`${this.name} moved ${distanceInMeters}m.`);
}
}
class Snake extends Animal {
constructor(name: string) {
super(name);
}
move(distanceInMeters = 5) {
console.log("Slithering...");
super.move(distanceInMeters);
}
}
let sam = new Snake("Sammy the Python");
sam.move();
interface Animal {
name: string;
eat(): void;
}
class Dog implements Animal {
name: string;
constructor(theName: string) {
this.name = theName;
}
eat() {
console.log(`${this.name} 吃狗粮。`)
}
}
class Cat implements Animal {
name: string;
constructor(theName: string) {
this.name = theName;
}
eat() {
console.log(`${this.name} 吃猫粮。`)
}
}
let dog: Animal;
dog = new Dog('狗狗');
dog.eat();
软件工程中,我们不仅要创建一致的定义良好的 API,同时也要考虑可重用性。 组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型,这在创建大型系统时为你提供了十分灵活的功能。
在像 C# 和 Java 这样的语言中,可以使用泛型来创建可重用的组件,一个组件可以支持多种类型的数据。 这样用户就可以以自己的数据类型来使用组件。
设计泛型的关键目的是在成员之间提供有意义的约束,这些成员可以是:类的实例成员、类的方法、函数参数和函数返回值。
泛型(Generics)是允许同一个函数接受不同类型参数的一种模板。相比于使用 any 类型,使用泛型来创建可复用的组件要更好,因为泛型会保留参数类型。
interface GenericIdentityFn<T> {
(arg: T): T;
}
function identity <T, U>(value: T, message: U) : T {
console.log(message);
return value;
}
console.log(identity<Number, string>(68, "Semlinker"));
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;
};
假如我想打印出参数的 size 属性呢?如果完全不进行约束 TS 是会报错的:
function trace<T>(arg: T): T {
console.log(arg.size); // Error: Property 'size doesn't exist on type 'T'
return arg;
}
报错的原因在于 T 理论上是可以是任何类型的,不同于 any,你不管使用它的什么属性或者方法都会报错(除非这个属性和方法是所有集合共有的)。那么直观的想法是限定传给 trace函数的参数类型应该有
size 类型,这样就不会报错了。如何去表达这个类型约束的点呢?实现这个需求的关键在于使用类型约束。 使用
extends`关键字可以做到这一点。简单来说就是你定义一个类型,然后让 T 实现这个接口即可。
interface Sizeable {
size: number;
}
function trace<T extends Sizeable>(arg: T): T {
console.log(arg.size);
return arg;
}
总体而言,TypeScript和Java很像很像