基础语法
基础类型和对象类型
// 基础类型 null, undefined, symbol, boolean, voidconst count: number = 123;const teacherName: string = 'Dell';// 对象类型class Person {}const teacher: {name: string;age: number;} = {name: 'Dell',age: 18};const numbers: number[] = [1, 2, 3];const dell: Person = new Person();const getTotal: () => number = () => {return 123;};
类型注解和类型推断
- type annotation (类型注解):我们来告诉 TS 变量是什么类型
- type inference (类型推断):TS 会自动的去尝试分析变量的类型
- 如果 TS 能够自动分析变量类型,我们就什么也不需要做了
- 如果 TS 无法分析变量类型的话,我们就需要使用类型注解 ```typescript // let count: number; // count = 123;
// let countInference = 123;
// const firstNumber = 1; // const secondNumber = 2; // const total = firstNumber + secondNumber;
function getTotal(firstNumber: number, secondNumber: number) { return firstNumber + secondNumber; }
const total = getTotal(1, 2);
const obj = { name: ‘dell’, age: 18 };
<a name="jnBUO"></a>### 函数类型```typescriptfunction add(first: number, second: number): number {return first + second;}// void 和 neverfunction sayHello(): void {console.log('hello');}function errorEmitter(): never { // never表示这个函数压根就执行不到最后throw new Error();console.log(123);}function loop(): never { // never表示这个函数永远不可能执行完成while(true) {}}// 解构类型function add({ first, second }: { first: number; second: number }): number {return first + second;}// 即使只解构一个,也要严格遵循结构注解语法function getNumber({ first }: { first: number }) {return first;}const total = add({ first: 1, second: 2 });const count = getNumber({ first: 1 });
小复习
// 基础类型, boolean, number, string, void, undfined, symbol, nulllet count: number; // 如果赋值和定义放在一行,ts可以自动推断出变量类型;但如果放在两行,ts就推断不出了!!!count = 123;// 对象类型, {}, Class, function, []const func = (str: string) => {return parseInt(str, 10);};// (str: string) => number 是函数的类型const func1: (str: string) => number = str => {return parseInt(str, 10);};const date = new Date();// 其他的 caseinterface Person {name: 'string';}const rawData = '{"name": "dell"}';const newData: Person = JSON.parse(rawData); // JSON.parse返回的数据ts会自动推断为any,所以需要写类型注解let temp: number | string = 123;temp = '456';
数组和元组
// 数组const arr: (number | string)[] = [1, '2', 3];const stringArr: string[] = ['a', 'b', 'c'];const undefinedArr: undefined[] = [undefined];// type alias 类型别名type User = { name: string; age: number };class Teacher {name: string;age: number;}const objectArr: Teacher[] = [new Teacher(),// 只要属性相同,ts也认{name: 'dell',age: 28}];// 元组 tupleconst teacherInfo: [string, string, number] = ['Dell', 'male', 18];// csvconst teacherList: [string, string, number][] = [['dell', 'male', 19], ['sun', 'female', 26], ['jeny', 'female', 38]];
interface接口
// interface 和 type 相类似,但并不完全一致// type是类型别名,相当于C语言里的typedef,type可以为基础类型起别名: type Position = number; 这语义化就相当优雅了~interface Person {// readonly name: string;name: string;age?: number;[propName: string]: any;say(): string;}interface Teacher extends Person { // 接口可以像类一样继承teach(): string;}interface SayHi { // 将函数定义成接口(word: string): string;}const getPersonName = (person: Person): void => {console.log(person.name);};const setPersonName = (person: Teacher, name: string): void => {person.name = name;};const person = {name: 'dell',sex: 'male', // 加一个sex字段,下面函数并不会报错(如果加方法就会报错了);但是如果直接将该对象传给函数,就会报错。// 因为:ts会对“对象字面量的直接赋值”做类型的强校验!如果将对象先赋给一个变量缓存一下,就可以解除强校验。// 那我如何让代码使用者在传对象字面量的时候也可以绕过类型强校验呢? 很简单,接口定义中加这个——[propName: string]: any;say() {return 'say hello';},teach() {return 'teach';}};getPersonName(person);setPersonName(person, 'lee');class User implements Person { // 像Java一样,接口是最高级的抽象。name = 'dell';say() {return 'hello';}}const say: SayHi = (word: string) => {return word;};
类型断言(Type Assertion)
类型断言可以用来手动指定一个值的类型,即允许变量从一种类型更改为另一种类型。即强制类型转换。
语法格式:
<类型>值
或:
值 as 类型
实例
var str = '1'var str2:number = <number>str //str、str2 是 string 类型console.log(str2)
面向对象
类的定义与继承
class Person {name = 'dell';getName() {return this.name;}}class Teacher extends Person {getTeacherName() {return 'Teacher';}getName() {return super.getName() + ' lee'; // super表示父类。super()表示调用父类的构造函数}}const teacher = new Teacher();console.log(teacher.getName()); // dell leeconsole.log(teacher.getTeacherName()); // Teacher
何时会用super?
答:当子类重写了父类的方法,此时却还想调用父类的该方法,就需要使用super。
构造函数与权限修饰符
权限修饰符
private, protected, public 访问类型
- public 允许我在类的内外被调用
- protected 允许在类内及继承的子类中使用
- private 允许在类内被使用
class Person {public name: string;public sayHi() {this.name;console.log('hi');}private sayABC() {console.log(this.name);}}class Teacher extends Person {public sayBye() {this.sayHi();}}const person = new Person();console.log(person.name); // 没问题,因为name是public属性person.name = 'dell'; // 也可以赋值person.sayHi(); // 这个也没问题person.sayABC() // 报错,因为sayABC()是private方法
readonly显示属性为只读
// readonlyclass Person {public readonly name: string;constructor(name: string) {this.name = name;}}const person = new Person('Dell');person.name = 'hello'; // 试图修改只读属性,报错!console.log(person.name);
构造函数
class Person {// 传统写法// public name: string;// constructor(name: string) {// this.name = name;// }// 简化写法constructor(public name: string) {}}const person = new Person('dell'); // dellconsole.log(person.name);
子类在继承父类时,一定要手动调用父类的构造函数实现实例化
class Person {constructor(public name: string) {}}class Teacher extends Person {constructor(name: string, public age: number) {// 使用super()手动调用父类的构造函数super(name);}}const teacher = new Teacher('yyy', 28);console.log(teacher.age); // 28console.log(teacher.name); // yyy
getter和setter
// getter and setterclass Person {constructor(private _name: string) { }// getget name() {return this._name + ' lee'; // 返回前可以做一些副作用, 如加密}// setset name(name: string) {const realName = name.split(' ')[0]; // 赋值前也可以做一些副作用, 如校验this._name = realName;}}const person = new Person('dell');console.log(person.name); // dell leeperson.name = 'dell lee';console.log(person.name); // dell lee
使用setter和getter除了可以产生副作用,也可以配合private修饰符实现对类属性的保护,其效果类似与readonly。
小实战:单例模式实现
class SingleCaseDemo {// 将创建的单例设置为static属性,且privateprivate static instance: SingleCaseDemo;// 核心:将constructor设为private!!!private constructor(public name: string) {}// 对外提供一个getter接口,借此产生副作用,确保实例唯一!public static getInstance() {if (!this.instance) {this.instance = new SingleCaseDemo('yyy');}return this.instance;}}const demo1 = SingleCaseDemo.getInstance();const demo2 = SingleCaseDemo.getInstance();console.log(demo1.name);console.log(demo2.name);
抽象类
// 抽象类abstract class Geometry {// 抽象类中能写具体的属性或者方法吗? 能!getType() {return 'Geometry';}// 下面定义了一个抽象方法。这意味着所有继承该类的非抽象子类都要实现该方法。abstract getArea(): number;abstract name: string;}class Circle extends Geometry {public name: string; // // 抽象属性具体化(此处name的修饰符要么是public,要么不写)public radius: numberconstructor(radius: number) {super() // 即使父类是抽象类,没有构造函数,也得写super()。即:只要是继承,就得叫爸爸this.radius = radiusthis.name = 'Circle'}// 抽象方法具体化(实现抽象方法)getArea() {return 3.14 * this.radius * this.radius;}}class Square extends Geometry {name: string // 抽象属性具体化public length: numberpublic width: numberconstructor(length: number, width: number) {super()this.name = 'Square'this.length = lengththis.width = width}// 抽象方法具体化(实现抽象方法)getArea(): number {return this.length * this.width;}}
抽象类可以对类做更高等级的抽象,接口亦是如此:
interface Person {name: string;}interface Teacher extends Person {teachingAge: number;}interface Student extends Person {age: number;}interface Driver {name: string;age: number;}const teacher = {name: 'dell',teachingAge: 3};const student = {name: 'lee',age: 18};const getUserInfo = (user: Person) => {console.log(user.name);};getUserInfo(teacher);getUserInfo(student);
那么问题来了,抽象类和接口,有什么区别呢?他们在实战中又担任着什么样的角色呢?慢慢体会吧~
