#【第一篇:写在前面】
(1) ts是微软开发并开源的编程语言,是js的超集,在js的基础上又进行了拓展,如interface、泛型、枚举等等。ts最大的核心就是为js增加了类型检查。
(2) ts与我们学的其他语言不同,如sass、vue、webpack等,这些语言的代码只要出现语法问题,必然会在浏览器或其他地方报出错误;但是ts不同,如果代码不符合ts语法但符合js语法,是仍然能运行的,因为ts最终是转成js运行的。
(3) 所以ts只是相对的为js增加了类型检查,而不是像其他语言如java一样,是绝对的类型校验。因此,使用ts开发需要开发人员主动的解决ts语法错误;说实在的,这对开发人员的自觉性有较高的要求。
- 举个例子
//Person类
class Person {
private name: string;
constructor(name:string) {
this.name = name;
}
}
let p = new Person('Tom');
console.log(p.name); //在ts中会提示:属性“name”为私有属性,只能在类“Person”中访问
//但是在js中,仍然能得到 Tom
(4) ts分为正常模式
和严格模式
,默认是正常模式;两种情况的有些表现有所不同,建议使用严格模式,否则可能与你预想的情况有所不同,特别是处女座的。
//tsconfig.json
{
"compilerOptions": {
"strictNullChecks": true, //管null和undefined严格的
"strict": true, //全部严格
}
}
#【第二篇:工具篇】
#安装工具
typescript: 首先,浏览器或nodejs是不能直接运行.ts文件的,需要安装.ts转换工具typescript
,typescript工具会将.ts文件转换成.js文件;
- 全局安装:
npm i typescript -g
- 安装到项目:
npm i typescript -D
ts-node: nodejs执行.ts文件时,是先使用typescript
工具将.ts文件转换成.js文件,再使用node ./xx.js
来执行;比较麻烦,我们可以使用ts-node
工具直接运行.ts文件(其实是其内部做了一层处理,本质还是使用node运行.js文件)
- 全局安装:
npm i ts-node -g
- 安装到项目:
npm i ts-node -D
#工具的使用
-
全局安装
- typescript:直接运行
tsc ./one.ts
,就能将.ts文件转成.js文件了 - ts-node:直接运行
ts-node ./one.ts
,直接在node输出内容了
- typescript:直接运行
-
安装到项目中
- 第一种:需要在命令前加
npx
,比如:npx tsc ./one.ts
或npx ts-node ./one.ts
- 第二种:需要在
package.json
中的scripts
下添加命令,如下:
//package.json { "scripts": { "tsc": "tsc ./one.ts", "ts-node": "ts-node ./one.ts" } } //执行npm run tsc ,等同于 tsc ./one.ts //执行npm run ts-node,等同于 ts-node ./one.ts
- 第一种:需要在命令前加
# typescript工具的 tsc 命令
typescript工具安装成功之后,会带有一个tsc
命令,来执行.ts的转换,常见命令如下:
-
tsc ./one.ts
:直接在当前目录生成转译的one.js文件,该命令只能转译单个文件,不能转译文件夹。 -
tsc --init
:生成tsconfig.json配置文件 -
tsc
:根据当前项目根目录的tsconfig.json文件进行转译 -
tsc --project tsconfig.production.json
:根据指定的配置文件进行转译,--project可简写为-p -
tsc --watch
:在tsc
命令下添加一个监听,--watch可简写为-w
# tsconfig.json配置文件
- 基础输入输出
{
"compilerOptions": {
"outFile": "",
"rootDir": "",
"outDir": ""
},
"files": [
"./one.ts",
"./ts/n1.ts"
],
"include": [],
"exclude": []
}
/*
compilerOptions:编译器配置,参数是对象,包含了很多编译器配置
files:参数是数组,指定需要编译的.ts文件;不要省略.ts后缀
生成的.js文件与.ts文件同名且在同一文件夹下;
include:参数是数组,指定需要编译的文件或文件夹;可以使用正则模糊指定;
会与files指定的文件取并集;
exclude:参数是数组,过滤不需要被编译的文件或文件夹;可以使用正则模糊过滤;
会过滤到include指定的文件或文件夹;但不会过滤掉files指定的文件;
---------------------------------------------------------------------------
compilerOptions下属性:
{
"outFile": '', //将多个相互依赖的文件生成一个文件,仅支持amd模块和system模块
"rootDir": '', //指定一个文件夹,里面是需要所有需要转换的.ts文件
//若是除了这个文件还有其他需要转换的ts文件,则会报错;此时可以使用exclude排除
"outDir": '', //指定一个文件夹,存放所有编译之后的.js文件
}
*/
- compileOptions常用配置
详见:https://www.jianshu.com/p/0383bbd61a6b
"compilerOptions": {
"incremental": true, // TS编译器在第一次编译之后会生成一个存储编译信息的文件,第二次编译会在第一次的基础上进行增量编译,可以提高编译的速度
"tsBuildInfoFile": "./buildFile", // 增量编译文件的存储位置
"diagnostics": true, // 打印诊断信息
"target": "ES5", // 目标语言的版本
"module": "CommonJS", // 生成代码的模板标准
"outFile": "./app.js", // 将多个相互依赖的文件生成一个文件,可以用在AMD模块中,即开启时应设置"module": "AMD",
"lib": ["DOM", "ES2015", "ScriptHost", "ES2019.Array"], // TS需要引用的库,即声明文件,es5 默认引用dom、es5、scripthost,如需要使用es的高级版本特性,通常都需要配置,如es8的数组新特性需要引入"ES2019.Array",
"allowJS": true, // 允许编译器编译JS,JSX文件
"checkJs": true, // 允许在JS文件中报错,通常与allowJS一起使用
"outDir": "./dist", // 指定输出目录
"rootDir": "./", // 指定输出文件目录(用于输出),用于控制输出目录结构
"declaration": true, // 生成声明文件,开启后会自动生成声明文件
"declarationDir": "./file", // 指定生成声明文件存放目录
"emitDeclarationOnly": true, // 只生成声明文件,而不会生成js文件
"sourceMap": true, // 生成目标文件的sourceMap文件
"inlineSourceMap": true, // 生成目标文件的inline SourceMap,inline SourceMap会包含在生成的js文件中
"declarationMap": true, // 为声明文件生成sourceMap
"typeRoots": [], // 声明文件目录,默认时node_modules/@types
"types": [], // 加载的声明文件包
"removeComments":true, // 删除注释
"noEmit": true, // 不输出文件,即编译后不会生成任何js文件
"noEmitOnError": true, // 发送错误时不输出任何文件
"noEmitHelpers": true, // 不生成helper函数,减小体积,需要额外安装,常配合importHelpers一起使用
"importHelpers": true, // 通过tslib引入helper函数,文件必须是模块
"downlevelIteration": true, // 降级遍历器实现,如果目标源是es3/5,那么遍历器会有降级的实现
"strict": true, // 开启所有严格的类型检查
"alwaysStrict": true, // 在代码中注入'use strict'
"noImplicitAny": true, // 不允许隐式的any类型
"strictNullChecks": true, // 不允许把null、undefined赋值给其他类型的变量
"strictFunctionTypes": true, // 不允许函数参数双向协变
"strictPropertyInitialization": true, // 类的实例属性必须初始化
"strictBindCallApply": true, // 严格的bind/call/apply检查
"noImplicitThis": true, // 不允许this有隐式的any类型
"noUnusedLocals": true, // 检查只声明、未使用的局部变量(只提示不报错)
"noUnusedParameters": true, // 检查未使用的函数参数(只提示不报错)
"noFallthroughCasesInSwitch": true, // 防止switch语句贯穿(即如果没有break语句后面不会执行)
"noImplicitReturns": true, //每个分支都会有返回值
"esModuleInterop": true, // 允许export=导出,由import from 导入
"allowUmdGlobalAccess": true, // 允许在模块中全局变量的方式访问umd模块
"moduleResolution": "node", // 模块解析策略,ts默认用node的解析策略,即相对的方式导入
"baseUrl": "./", // 解析非相对模块的基地址,默认是当前目录
"paths": { // 路径映射,相对于baseUrl
// 如使用jq时不想使用默认版本,而需要手动指定版本,可进行如下配置
"jquery": ["node_modules/jquery/dist/jquery.min.js"]
},
"rootDirs": ["src","out"], // 将多个目录放在一个虚拟目录下,用于运行时,即编译后引入文件的位置可能发生变化,这也设置可以虚拟src和out在同一个目录下,不用再去改变路径也不会报错
"listEmittedFiles": true, // 打印输出文件
"listFiles": true// 打印编译的文件(包括引用的声明文件)
}
#【第三篇:基础类型】
#数据类型
ts是js的超集,包含js所有的数据类型;并拓展了一些类型。
ts是静态类型,类型是确定的,一旦赋值之后不可随意更改;
js是动态类型,类型是不确定的,赋值之后也可随意更改。
-
js数据类型
- 基础数据类型:boolean、number、string、null、undefined、symbol
- 引用数据类型:object
-
ts数据类型
- boolean、number、string、null、undefined、symbol
- object
- any、void、never
- 元组、数组
- 枚举(enum)、接口(interface)
1. 布尔值 (boolean)
let bool: boolean = true;
2. 数字 (number)
支持整数、浮点数,支持十进制、十六进制、八进制、二进制
let num: number = 66;
let num2: number = 0.99;
3. 字符串 (string)
支持模板字符串
let str: string = 'hello world';
//模板字符串
let age: number = 24;
let Name: string = 'Tom';
let word: string = `my name is ${Name}, 明年我就${age + 1}岁了`;
4. null 、undefined
- 默认情况下是除
never
类型之外所有类型的子集,包括数组、元祖等; - 严格模式下:
开启--strictNullChecks
null能赋值的类型有:any、null
undefined 能赋值的类型有:any、undefined、void
/* 默认情况下 */
//赋值给数组
let arr: number[] = null;
arr = undefined;
//赋值给元祖
let yy:[boolean, string] = undefined;
yy = null;
/* 严格模式下 */
//以上会报错,以下三种是正确的。
let nu: null = null;
let un: undefined = undefined;
let vo: void = undefined;
5. object
- object表示非原始类型,也就是除number,string,boolean,symbol,
null
或undefined之外的类型。 - 上面是说的严格模式,非严格模式下null、undefined是object的子集。
- 另外,虽然在js中,
typeof null === object
,但是在严格模式下,null不能赋值给object的类型。
/* 严格模式 */
let obj: object = []; // 正确
obj = null; // 不能将类型“null”分配给类型“object”
6. any
表示任意类型
注意:① any其实是移除类型检查,在对现有代码进行改写的时候非常有用;② 使用any移除类型检查,显然违背了ts的初衷;另外使用any会丢失一些信息,让类型推论变得诡异;除非特殊情况,不建议使用any
let an: any = 123;
an = true;
an = [1,2];
7. void
- 表示没有任何类型,一般用于表示函数没有任何返回值;
- 约束变量时,只能赋值:<默认>undefined、null,<严格>undefined
function ff(): void {
console.log('我没有返回值');
}
8. 元组
元组类型允许表示一个已知元素数量和类型的数组,各元素的类型不必相同。
let arr:[number, string] = [12]; //错误,少元素
let arr:[number, string] = [12, 'aa', true]; //错误,多元素
let arr:[number, string] = [true, 'aa']; //错误,类型不对
let arr:[number, string] = [123, 'aa']; //正确
9. 数组
TypeScript像JavaScript一样可以操作数组元素;有三种方式可以定义数组。
- 第一种:元素类型 + [ ]
- 第二种:使用数组泛型,Array<元素类型>
- 第三种:使用接口interface
//第一种
let arr: number[] = [];
//第二种
let arr2: Array<number> = [];
//第三种
interface Iarr {
length: number;
[index: number] : number;
}
let arr3: Iarr = [];
- 使用的类型元素,可以是任意元素类型,包括interface定义的类型;
//数字
let arr: number[] = [];
//null、undefined或void,但是没有多少意义
let arr: null[] = [null];
//数组
let arr: string[][] = [ ['a'] ];
//接口
interface Ia {
name: string
}
let arr: Ia[] = [ { name: 'Tom' } ];
#【第四篇:高级类型】
#类型注解
我们手动为变量声明类型,就是类型注解。
//声明num的类型是number
let num:number;
num = 123;
#类型推论
(1) 如果你没有对变量进行注解,ts会根据赋值自动进行推论。如果一个变量的类型,ts会自动推论出,则可以不进行类型注解。
(2) 类型推论帮助我们保持代码精简和高可读性。
- 如果一个变量只声明,未注解,未赋值,则推论为
any
let nn; // => let nn: any;
nn = 123;
nn = true;
nn = 'string';
- 函数的形参,未注解,则推论为
any
function ff(n) { // => n: any
console.log(n)
}
- 一个变量,未注解,但是赋值了,则推论为所赋值的类型 (除null、undefined)
- 若被赋值的是null或undefined,则推论为
any
let n = 123; // => let n: number = 123;
//null或undefined
let nu = null; // => let nu: any = null;
let un = undefined; // => let un: any = undefined;
1. 类型推论的应用
//有一个函数表达式如下:
let ff: (pram1: number, pram2: string) => number = function(a: number, b: string): number {
return a
}
//其实已经对变量ff进行注解了,ts能够推论出后面的function的类型
//可以简化如下:
let ff2: (pram1: number, pram2: string) => number = function(a,b) {
return a;
}
//当然,使用interface还可以简化:
interface Ifn {
(pram1: number, pram2: string) : number;
}
let ff3: Ifn = function(a, b) {
return a;
}
#联合类型
联合类型(Union Types)表示一个值可以是几种类型之一; 使用竖线 |
分隔每个类型;类型可以是任何的类型,包括interface创建的类型等。
/* 基础类型 */
let nn: number|string|boolean;
nn = 123;
nn = 'hello';
nn = true;
/* interface创建的类型 */
interface Itp {
name: string
}
let mm: number|Itp;
mm = 123;
mm = { name: 'Tom' };
联合类型使用成员共有属性或方法不会报错;但是如果使用了非共有的属性,则会根据类型推论在赋值时判断。
/* 函数中如下写,一定会报错;可以使用类型断言,见下: */
function fn(param: number|string):number {
return param.length; //报错,类型“string | number”上不存在属性“length”
}
/* 根据赋值情况来判断是否正确 */
let nn: number|string;
nn = 'abc';
console.log(nn.length); //不报错
nn = 123;
console.log(nn.length); //报错, 类型“number”上不存在属性“length”
#类型断言
类型断言(Type Assertion)可以用来手动指定一个值的类型。经常与联合类型一同使用。
- 语法一:
<类型>值
- 语法二:
值 as 类型
在 tsx 语法(React 的 jsx 语法的 ts 版)中必须使用值 as 类型
。
function ff(param:number|string):number {
return (<string>param).length;
//或者
return (param as string).length
}
#类型别名
类型别名就是给某个类型或联合类型起一个新的名字;起到简化的作用;使用关键字type
定义类型别名;类型可以是任何的类型,包括interface创建的类型等。
//把 number 取名为 Tnum
type Tnum = number;
let nn: Tnum = 123;
//为联合类型取名为 Tuni
type Tuni = number | string | boolean;
let mm: Tuni = 123; //等同于:let mm: number|string|boolean = 123;
mm = 'hello';
//联合类型中有interface创建的类型,取名为 Tinter
interface Iobj {
name: string
}
type Tinter = number | Iobj;
let vv: Tinter = 123;
vv = { name: 'Tom' };
1. 类型别名与接口的区别
- 类型别名可以理解为对联合类型的简化写法,本身不创建新的类型,也不能 extends和 implements其它类型;
- 而接口是创建一个新的类型,能 extends和 implements其它类型;
#【第五篇:检查赋值】
ts的核心就是类型检查,学过js的我们都知道这个类型可以是 boolean、string、number等等;但是ts不仅可以约束所赋值的数据类型,还可以对所赋的值进行约束。
其实不论是具体的数据类型还是具体的值,在ts中都叫做类型检查;只是我们不是很理解罢了,总之能使用数据类型的地方也能使用具体的值(我没全部测试,我觉得应该是);测试如下:
//sex变量只能赋值 男
let sex: '男';
sex = '男';
sex = '女'; //错误
(1) 使用联合类型
let sex: '男'|'女'|'保密';
(2) 使用类型别名
type show = '优秀' | '良好' | '及格' | '很差';
let d: show = '优秀';
(3) 使用interface
interface Istr {
name: 'Tom' | 'Mike' | 'Leo',
work: '优秀' | '良好' | '及格' | '很差'
}
//使用
let person1: Istr = {
name: 'Mike',
work: '优秀'
}
let person2: Istr = {
name: 'Tom',
work: '及格'
}
# 【第六篇:接口(interface)】
TypeScript的核心原则之一是对值所具有的结构进行类型检查。我们可以使用boolean、number、string等对一个普通变量进行检查。
let boo: boolean = true;
let num: number = 123;
let str: string = '456';
但是,如果我们需要一个对象,里面必须有name、age属性,并且name是string类型,age是number类型;那我们上面的方法就无法实现如此复杂的操作了,此时我们可以使用interface来定义一套检查规范。
interface可以理解为自定义的类型规范,可以同string、number等一样使用。
//定义接口
interface Itype {
name: string,
age: number
}
//使用接口进行类型检查
let t: Itype = {
name: 'Tom',
age: 25
}
其实像boolean、number、string等其实是一种简单类型检查规范,interface能将这些简单的规范组合在一起,定义更加复杂的规范。
1. 对象类型的接口
接口可以定义一类对象的形状(即属性或方法);变量对象的形状必须与接口的形状保持一致,即属性名、属性类型、所有属性一一对应。接口可以定义必选属性、可选属性、只读属性、任意属性;
- 必选属性
interface Imust {
name: string
}
//定义的变量对象必须有值是string类型name属性,且不允许有其他属性
let prop: Imust = {
name: 'Tom'
}
- 可选属性
interface Ichoice {
name : string,
age? : number //可选属性
}
//定义变量对象 -- 无age属性
let prop: Ichoice = {
name: 'Tom'
}
//定义变量对象 -- 有age属性
let prop2: Ichoice = {
name: 'Tom',
age: 23
}
- 只读属性
interface Iread {
name: string,
readonly sex: string //只读属性
}
//定义的变量对象中的sex属性只能读取,不能重新赋值
let prop: Iread = {
name: 'Tom',
sex: '男'
}
prop.sex = '女'; //报错
- 任意属性
interface Iany {
[propName: string]: boolean
}
/*
propName:代指属性名,取值任意,但尽量语义化
[string]:指定变量对象的属性名的类型是字符串类型
boolean: 指定变量对象的属性值的类型是布尔类型,【A】
注意:这种写法不是只能赋值一个属性,而是可以赋值n个属性;可以理解为正则匹配
*/
let obj: Iany = {
a: true,
b: false
}
//注释A处,这里的类型可以是任何类型
//但是,一些必须属性的类型能限制它的类型,如下
interface Iany2 {
name: string,
[propName:string]: //B
}
//B处只能选择 string或any,因为 propName 可能是 name
//而name只能赋值string类型的值,而不能赋值其他类型的值
(2) 使用任意属性来约束数组
//使用接口能约束数组,但是也可能是伪数组
interface Iarr {
length: number,
[index: number]: boolean
}
//数组
let arr: Iarr = [true, false];
//伪数组
let obj: Iarr = {
length: 2,
0: false,
1: true
}
2.函数类型的接口
为函数表达式的变量添加约束。
interface Ifn {
(pram1: string, pram2: number) : boolean;
}
/*
+ pram1、pram2单纯为了指定赋值的函数体的形参数量和数据类型;在赋值的函数体中的形参名是自定义的
+ 表示函数体中最多有2个形参,且参1必须是string类型,参2必须为number类型
+ 若参数没有指定形参的类型,则默认参1为string类型,参2为number类型
+ 可以不传参数或只传一个参数
+ boolean处为指定函数体的返回值,若非any、void类型或不填,则必须指定函数体的该类型的返回值
+ 遵循严格模式 --strictNullChecks
*/
//指定返回值,无形参
let f1: Ifn = function(): boolean {
return true;
}
//指定返回值,一个形参
let f2: Ifn = function(name: string): boolean {
return true;
}
//指定返回值,2个形参,但不指定类型
let f3: Ifn = function(name, age): boolean {
return true;
}
3.接口的继承 (extends)
interface Ia {
name: string
}
//Ib接口继承Ia接口
interface Ib extends Ia {
age: number
}
//定义变量对象
let n: Ib = {
name: 'Tom',
age: 24
}
#【第七篇:函数注解】
1. 函数声明式
function getBar(name: string, age: number): string {
return `My name is ${name}, ${age}岁了!`
}
//调用时,需要让参数的类型和数量保持一致,否则会报错
getBar('Tom', 25);
2. 函数表达式
/* 第一种:只注解函数 */
let getBar = function(name:string, age:number):string {
return name + age;
}
getBar('Tom', 34);
/* 第二种:全部注解 */
let getBar2: (param1:string, param2: number) => string = function(name:string, age:number): string {
return name + age;
}
getBar2('Tom',66);
/* 第三种:根据类型推论,第二种可以简写如下: */
let getBar3: (param1:string, param2:number) => string = function(name,age) {
return name + age;
}
getBar3('Tom',66);
/* 第四种,将变量左边用interface简化 */
interface Ifn {
(param1:string, param2:number) : string;
}
let getBar4:Ifn = function(name,age) {
return name + age;
}
getBar4('Tom', 88);
(1) 函数表达式的函数变量说明:
let getFoo: (param1:string, param2: number) => string;
/*
getFoo的值必须是一个函数,
该函数可以有0-2个参数,参数名自定义,
参数的类型第一个必须是string,第二个必须是number,
返回值是string
*/
(2) 使用联合类型时,需要使用 () 括起来,否则会出错
//表示函数或number类型
let obj: ( (param:string) => string ) | number;
(3) 函数调用时,所传的参数数量和类型必须跟function注解时的数量和类型一致,否则会报错。
3. 默认参数
function fn(name = 'Tom'): string {
return name;
}
4. 可选参数
ts里的每个函数参数都是必须的;但是在js里,每个参数都是可选的,可传可不传;在ts里我们可以在参数名旁使用?
实现可选参数的功能。
注意:① 可选参数必须放到必须参数之后;② 可选参数不能使用默认参数
function getBar(name: string, age?: number): string {
return name;
}
//调用
getBar('Tom');
getBar('Tom', 24);
5. 剩余参数
注意:剩余参数变量是数组类型
function getFoo(a:string, b?: number, ...args:any[]):void {
console.log(a); //aaa
console.log(2); //2
console.log(args); //[ 3, 4, 5 ]
}
getFoo('aaa',2,3,4,5);
6. 重载
重载就是让代码看着更清楚,语意更明确而已
function sum(n: number): number;
function sum(n: string): string;
function sum(n: number|string): number|string {
if(typeof n === 'number') {
return n + 99;
}else{
return n + 'Hello World!'
}
}
console.log(sum(1)); //100
console.log(sum('Tom_')); //Tom_Hello World!
[TOC]
# 【第八篇:class】
1. 实例私有属性、方法
注解实例私有属性、方法需要先在class内部声明;
class Person {
name: string; //注解实例私有属性
say: () => void; //注解实例私有方法
constructor(name: string) {
this.name = name;
this.say = function() {
console.log('My name is', this.name)
}
}
}
let p = new Person('Tom');
p.say();
2. 继承
继承来的属性或方法不需要在class内部进行注解了。
/* 父类 */
class Person {
name: string;
constructor(name:string) {
this.name = name;
}
}
/* 子类 */
class Man extends Person {
//name:string; //这里的name是不需要注解的,因为这是从父类继承过来的
age:number; //这里的age是自己的属性,需要进行注解
constructor(name:string, age:number) {
super(name);
this.age = age;
}
}
let m = new Man('Tom',55);
console.log(m);
3. 只读修饰符
class Person {
readonly name:string;
constructor(name:string) {
this.name = name;
}
}
let p = new Person('Tom');
p.name = 'Mike'; //无法分配到 "name" ,因为它是只读属性
4. 公共,私有与受保护的修饰符
(1) ts规定class成员有三种修饰符,public、protected、private;默认是public
(2) public:当前类内部,子类内部,实例对象均可使用
(3) protected:当前类内部,子类内部可使用,实例对象均不可使用
(4) private:仅在当前类内部可以使用,子类,实例对象均不可使用
注意:① 在实例对象中使用了private修饰的属性,也仅仅是在ts中提示错误,仍然能正确编译成js
② ts中的private与es6中的 # 是有本质区别的;但是如果遵循ts的错误提示,是可以起到相同作用的。
- public
/* 父类 */
class Person {
public name: string;
public constructor(name:string) {
this.name = name;
}
public perSay() {
console.log('public属性:name',this.name);
}
}
/* 子类 */
class Man extends Person {
public constructor(name:string) {
super(name);
}
public manSay() {
console.log('继承父类的public属性:name',this.name);
}
}
//父类实例化
let p = new Person('Tom');
console.log(p.name); //正常
p.perSay(); //正常
//子类实例化
let m = new Man('Mike');
console.log(m.name); //正常
m.manSay(); //正常
- protected
/* 父类 */
class Person {
protected name: string; //name属性是受保护的
public constructor(name:string) {
this.name = name;
}
public perSay() {
console.log('protected属性:name',this.name);
}
}
/* 子类 */
class Man extends Person {
public constructor(name:string) {
super(name);
}
public manSay() {
console.log('继承父类的protected属性:name',this.name);
}
}
//父类实例化
let p = new Person('Tom');
console.log(p.name); //错误,父类实例不能访问
p.perSay(); //正常
//子类实例化
let m = new Man('Mike');
console.log(m.name); //错误,子类实例不能访问
m.manSay(); //正常,manSay方法是使用的子类Man内部的name属性
- private
/* 父类 */
class Person {
private name: string; //name属性是私有的
public constructor(name:string) {
this.name = name;
}
public perSay() {
console.log('private属性:name',this.name);
}
}
/* 子类 */
class Man extends Person {
public constructor(name:string) {
super(name);
}
public manSay() {
console.log('继承父类的private属性:name',this.name); //错误,见下
}
}
//父类实例化
let p = new Person('Tom');
console.log(p.name); //错误,父类实例不能访问
p.perSay(); //正常,父类内部可以访问
//子类实例化
let m = new Man('Mike');
console.log(m.name); //错误,子类实例不能访问
m.manSay(); //错误,子类不能使用
5. 静态属性、静态方法
class Person {
static isNa = '静态属性';
static isRead = function() {
console.log('静态方法', Person.isNa);
}
}
Person.isRead()
6. 抽象类
抽象类做为其它派生类的基类使用;不能直接被实例化。
abstract class Person {
name: string;
constructor(name) {
this.name = name;
}
}
let p = new Person('Tom'); //无法创建抽象类的实例。
#【第九篇:泛型】
(1) 当我们写好一个API后,不仅需要支持当前的数据类型,同时也能支持未来的数据类型;泛型就给予了这样的灵活性。
(2) 当我们定义接口或类时,如果遇到数据类型不明确时,就可以使用泛型。
//无复用性,只能传入number类型
function foo(arg:number): number {
return arg;
}
foo(123);
//使用any虽然实现了复用性;[本质上是移除类型检查]
//但是我们只知道可以传入任意类型参数,也会返回任意类型值
//但是不能保证传入的参数与返回的值类型相同
//比如我们传入一个数字,我们只知道任何类型的值都有可能被返回
//这样让整个api变得诡异,不建议使用any
function foo2(arg:any): any {
return arg;
}
//此时我们就可以使用泛型
function foo3<T>(arg: T): T {
return arg
}
1. 如何定义泛型
定义泛型是使用尖括号, <类型变量>
,类型变量是一种特殊的变量,只用于表示类型而不是值;类型变量是自定义名,但一般使用大写字母。
2. 函数中使用泛型
- 定义泛型
可以定义一个泛型,也可以定义多个泛型。
//定义一个泛型
//泛型是 T, 参数arg类型是T, 返回值类型是T
function foo<T>(arg: T): T{
return arg;
}
//定义多个泛型
//泛型是T、K, 参数name类型是T、age类型是K, 返回值类型是K
function bar<T, K>(name:T, age:K): K {
return age;
}
- 使用泛型
可以使用尖括号(< >)来明确地传入类型,也可以使用类型推论而省略;
function foo<T>(arg: T): T{
return arg;
}
//传入类型
let out = foo<string>('Hello');
//使用类型推论
let out2 = foo('World');
- 使用泛型变量,把泛型当做类型的一部分,
Array<T>
//定义了泛型T,参数names类型是泛型T的数组,返回值类型是number
function getLen<T>(names:Array<T>): number {
return names.length;
}
3. 类中使用泛型
//Person类中定义泛型T
//实例属性使用泛型T
class Person<T> {
name: T;
constructor(name:T) {
this.name = name;
}
}
//传入类型
let p = new Person<string>('Tom');
//不传入类型
let p2 = new Person(123);
4. 接口中使用泛型
接口中使用泛型与其他最大的区别就是,需要使用尖括号(< >)来明确地传入类型。
interface Inter<T>{
(param: T): T;
}
//使用时,必须传入类型
let ff:Inter<string> = function(name) {
return name;
}
5. 泛型约束
我们也可以为泛型增加相应的约束,比如我们希望传入的参数必须有length属性,如下实现:
interface Iarr {
length: number
}
function getLen<T extends Iarr>(names: T):number {
return names.length
}
//使用
getLen(123); //报错,数字没有length属性
getLen('Hello'); //ok
# 【第十篇:枚举 enum】
(1) 枚举是ts对js标准数据类型的拓展,枚举是只读属性
;
(2) 我们在对接后台时,常常会遇到这样的场景,当请求数据时 0:fail, 1:success, 2:error,此时就可以使用枚举。
enum s {
fail = 0,
success = 1,
error = 2
}
1. 数字枚举
(1) 数字枚举默认从0开始,依次递增
//上面的例子等同于
enum s {
fail = 0,
success = 1,
error = 2
}
(2) 数字枚举赋值的变量下面依次递增
enum s {
fail,
success = 20,
error
}
//等同于
enum s {
fail = 0,
success = 20,
error = 21
}
(3) 可以使用外部变量或函数,但是下面的值需要赋初始值
function getNum():number {
return 11;
}
enum s {
fail = getNum(),
success, //枚举成员必须具有初始化表达式
error
}
//修改如下:
function getNum():number {
return 11;
}
enum s {
fail = getNum(),
success = 1,
error
}
(4) 数字枚举是反向映射的
,即你可以通过属性名找到值,也可以根据值找到属性名
enum s {
fail = 0,
success = 1,
error = 2
}
console.log(s.success); // 1
console.log(s[1]); // success
2. 字符串枚举
(1) 字符串枚举没有默认值,也不会自增;必须手动赋初始值。
enum s {
fail = 'fail',
success = 'success',
error = 'error'
}
(2) 字符串枚举没有反向映射
;
enum s {
fail = 'f',
success = 's',
error = 'e'
}
console.log(s.fail); // f
console.log(s.f); // 类型“typeof s”上不存在属性“f”
3. 异构枚举
异构枚举就是一个枚举类型中既有数字枚举,也有字符串枚举;官网不推荐使用异构枚举。