课程目标
- TS
知识点
- TS语法及用法
参考
TypeScript中文官网地址: www.tslang.cn/
TypeScript
TypeScript是JavaScript的超集,支持ECMAScript6标准TypeScript是由微软开发的自由和开源的编辑语言- 可以编译成纯
javascript,编译出来的javascript可以运行在任何浏览器上
TypeScript和JavaScript的区别?
TypeScript是JavaScript的超集,扩展了JavaScript的语法,现在有JavaScript代码可与TypeScript一起工作,TypeScript通过类型注解提供编译时的静态类型检查TypeScript可处理已有的JavaScript代码,只对其中的TypeScript代码进行编译- ts在开发时就能给编译错误,js错误只能在运行时体现。
- 作为强类型的语言,可以明确知道所有数据的类型。
- ts是面向对象的语言,包含了类、接口的概念。
安装TypeScript
npm install -g typescript
- 安装成功后可以使用
tsc命令来执行TypeScript的相关代码,比如查看版本号:
tsc -v
- 创建一个ts文件,以
.ts为后缀名,比如app.ts - 将
TypeScript转换为JavaScript,命令行:
tsc app.ts
- 使用node命令来执行app.ts文件(先安装node,可以去官网下载node安装包)
- 安装node
node官网地址:nodejs.org/zh-cn/
- 下载相对应的系统版本安装包
node中文下载安装包地址:nodejs.cn/download/
- 安装成功后就可以在命令行中访问
node可执行程序
node app.ts
基础类型
number string boolean symbol null array object undefined
TS新增类型
1.枚举(enum)
用关键字enum来定义枚举类型,一般最常用于定义常量,支持数字和基于字符串的枚举。
- 数字枚举
enum Direction {
Up = 1,
Down,
Left,
Right
}
定义了一个数字枚举,Up初始化为1,其余的成员会从1开始自动增长,换句话说Direction.Up的值为1,Down为2,Left为3,Right为4,也可以全部赋值
- 如果不使用初始化
enum Direction {
Up,
Down,
Left,
Right
}
Up初始化为0,其余的成员会从0开始自动增长,Direction.Up的值为0,Down为1,Left为2,Right为3
- 枚举还有一个便利就是可以根据值得到它的名字,如果我们知道数值是2,但又不确定它映射到Direction里的哪个名字
enum Direction {
Up=2,
Down=4,
Left=9,
Right=12
}
let typeparam: string = Direction[2]//输出:Up
- 字符串枚举
// js写法
const obj = {
'RUN':'run',
'EAT':'eat'
}
// 枚举
enum ActionType {
/**跑 */
Run = 'run',
/**吃 */
Eat = 'eat'
}
const a =ActionType.Eat //eat
2.type
// type
type Action = 'eat' | 'run'
const b: Action = 'eat'
3.interface接口
可选属性:接口里的字段不全都是必须,在字段名字定义的后面加?符号
- 可选属性的好处:可以对可能存在的属性进行预定义,可以捕获引用了不存在的属性时的错误
只读属性:一些对象属性只能在对象刚刚 创建的时候个性其值,可以在属性名前用readonly来指定只读属性。
//interface 接口
interface actResponse {
id: string,
name: string,
readonly age: number,
address?:string
}
/**
* 定义的接口引用时,接口定义的所有字段必须写全
* 不能漏一个字段属性,也不能新增字段属性,不然就报错
* 可以把字段写成可选属性:加一个问号代表字段可选,比如address?:string
*/
const c: actResponse = {
name:'',
id:'',
age:18
}
//只读属性
const per: actResponse = {id:'',name:'',age:18}
per.age = 20;//error 无法匹配到'age',因为是只读属性
- 接口继承:接口可以通过其他接口来扩展自己。允许接口继承多个接口,使用关键字
extends,继承的各个接口使用逗号,分隔
interface A {
name: string;
}
interface B {
sex: number;
}
interface C extends A, B{
age: number
}
const person: C = {name : 'xxx', sex : 1, age: 18}
console.info(`name:${person.name},sex:${person.sex},age:${person.age}`);//name:xxx,sex:1,age:18
4.联合类型
联合类型:|(多个类型或的关系,赋值时可以根据设置的类型来赋值)
只能赋值指定的类型,如果赋值其它类型就会报错
interface A {
name: string;
}
interface B {
sex: number;
}
function test1(a: A | B){
}
// 联合类型下面三种情况都可以
test1({sex: 0,name: "xiaoming"})
test1({sex: 0})
test1({name: "xiaoming"})
// 报错 address没有定义不能使用
test1({sex: 0,name: "xiaoming",address: "xxx"})
5.交叉类型
交叉类型 & (多个类型的合并类型,缺一不可)
interface A {
name: string;
}
interface B {
sex: number;
}
function test2(a: A & B){
}
test2({sex: 0,name: "xxx"})
// 报错
test2({sex: 0})
test2({name: "xxx"})
6.typeof
typeof:可以用来获取一个变量的声明
function toArray(x: number): Array<number>{
return [x]
}
type func = typeof toArray;
7.keyof
keyof:用来获取一个对象中的key
interface person {
name: string;
age: number;
}
8.in
in : 遍历枚举类型
/**
* in
* 比如把一个联合类型的所有值类型都是一样的
* number也可以指定any
*/
type keys="a" | "b" | "c";
type Obj = {
[key in keys] : number;
}
const objkeys: Obj = {
a:1,
b:2,
c:3
}
9.泛型
语法:
<T>
泛型主要是为了确保准确性,可以适用于多个类型,不同于any。不确定输入的类型是什么,又希望传入的是什么类型返回的就是什么类型。看个例子:
function identity<T>(arg: T): T { return arg; }
let identityFun = identity<number>(1)//1
//报错 类型number的参数不能赋值给string参数
let identityFun1 = identity<string>(12)//error
//是可以的
let identityFun1 = identity<string>('12')//12
10.泛型类
class GenericNumber<T> {
zero : T;
add: (x: T, y: T) => T;
}
let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zero = 0;
myGenericNumber.add = function(x,y) {
return x + y
}
例子中GenericNumber限制它只能使用number类型,也可以换成其他类型,把泛型类型放在类后面,可以确认类的所有属性都在使用相同的类型。
11.extends
主要用来约束泛型
// 例一
interface AE<T extends number> {
a: T;
}
const be: AE<number> = {a : 111}
// 例二
type acti = "eat" | "run"
interface AEC<T extends acti> {
a: T;
}
//bec的值只能是acti定义的a或b 写其他值会报错
const bec: AEC<acti> = {a: "eat"}
// 例三
interface lengthwise {
length :number;
}
// logginidentity约束T必须是lengthwise
function logginidentity<T extends lengthwise>(arg: T): T{
return arg;
}
// 如果没有写length属性就会报错
logginidentity({ length:10, value:1, id: '1111'})
12.Partial和Required
Partial:把所有选项完全变成可选项
Required:把所有选项完全变成必选项
interface pen {
name: string;
age: number;
address: string;
}
type b = Partial<pen>//把所有选项完全变成可选项
type c = Required<pen>//把所有选项完全变成必选项
13.类
TypeScript是面向对象的JavaScript。类描述了所创建对象共同的属性和方法,类使用关键字class来定义。
- 语法:
class class_name {}
类包含几个模块:
- 字段:类声明的变量
- 构造函数:类实例化时调用
- 方法:对象要执行的操作
- 栗子
class Greeter {
//字段
greeting: string;
//构造函数
constructor(message: string) {
this.greeting = message;
}
//方法
greet() {
return `hello,${this.greeting}`
}
}
//实例化对象
let greeter = new Greeter('world')
//访问方法
greeter.greet();
14.类的继承
使用关键字extends实现继承,不支持继承多个类,但支持多重继承(A继承B,B继承C)
class Root {
str: string;
}
class Child extends Root {}
class Leaf extends Child {}
const obj1 = new Leaf();
obj1.str = 'hello';
console.info(obj1.str);//hello
15.继承类的方法重写
类继承后,子类可以对父类的方法重新定义,称之为方法的重写,其中super关键字可以引用父类的属性和方法
class Root {
str: string;
doPrint(): void {
console.info('父类的方法');
}
}
class Child extends Root {
doPrint(): void {
super.doPrint();//调用父类的函数
}
}
16.类static关键字
static关键字用于定义类的数据成员(属性和方法)为静态的,静态成员可以直接通过类名调用。
class Root {
static str: string;
static doPrint(): void {
console.info('父类的方法');
}
}
Root.str = 'hello'
17. 类访问控制修饰符
public(默认):公有,可以在任何地方被访问。protected:受保护,可以被其自身及子类访问。private:私有,只以被当前所在类访问。
class Root2 {
str: string;
protected str1: string;
private doPrint(): void {
this.str1 = 'hello'
}
}
const root2 = new Root2()
//error doPrint是私有属性,只能在Root2类里访问;
root2.doPrint();
//error str1是受保护的,只能在Root2类及子类里访问;
root2.str1
18.类和接口
类可以实现接口,使用关键字implements
interface Loan {
a: number;
}
class Agri implements Loan {
a: number;
}
Agri实现Loan接口后,a属性没定义或属性类型不对就会报错。
19.Omit和Pike
- Pike:选取类型中指定类型,返回值是
interface Loan1 {
a: number;
b: number;
c: string;
d: boolean
}
type contact = Pick<Loan1, "b">
type contact = {b: number;}
- Omit:去除类型中某些项,去除多个字符中以
|分隔
interface Loan1 {
a: number;
b: number;
c: string;
d: boolean
}
type contact = Omit<Loan1, "b" | "a">
type contact = { c: string; d: boolean }
20.TS模块
- 模块在其自身的作用域里执行,而不是在全局作用域里。
- 两个模块之间的关系是通过在文件级别上使用
import和export建立的。 - 任何包含顶级
import或者export的文件都会被当成一个模块,如果一个文件不带项级的import或者export声明,会被视为全局。
- 导出
任何声明(比如变量、函数、类、类型别名或接口)都可以通过添加
export关键字来导出。
//ZipValidator.ts文件
export class Validator {
isShow (b: boolean) {
return b
}
}
- 如果需要对导出的部分重命名,可以用关键字
as
//ZipValidator.ts文件
class Validator {
isShow (b: boolean) {
return b
}
}
export {Validator as Form}
- 一个模块可以包含多个模块,联合导出可以用
*来表示
export * from'./ZipValidator'
- default
如果模块中用了
default关键字,import时可以不需要{}来包裹,直接引用,如果没用default关键字就需要用{},并且一个模块只能有一个default导出。
//maths.ts
export let pi = 3.14;
export function absolute() {
return pi;
}
//引用文件
import {pi, absolute} from './maths'
//maths.ts
export default let pi = 3.14;
//引用文件
import pi from './maths'
- type exprot 不能new,如果只需要一个类型,不需要new,实例 可以考虑用type。
//animal.ts
export type Cat = {breed: string; birth: number}
export interface Dog {}
//引用文件 两种方式都可以
import {Cat} from './animal.ts'
import type {Cat} from './animal.ts'
- 导入
用关键字
import来导入
import { ZipValidator } from 'ZipValidator'
let val = new ZipValidator();
- 模块相关配置项:module
- ESNext: 代表ES最新版本
- UMD/AMD
- ES2015
- ES2020
- 模块相关配置项:moduleResolution模块解析,一般用node,但node不是默认项。
- node:
- classic:
- 模块解析 模块解析是指编译器在查找导入模块内容时所遵循的流程
- 首先编译器会尝试定位表示导入模块的文件,会遵循二种策略:
classic或node,这些策略会告诉编译器到哪里找module. - 如果解析失败并且模块名是非相对的,编译器会尝试定位一个
外部模块声明
根据模块引用是相对的还是非相对的,模块导入会以不同的方式解析。
相对导入:是相对于导入它的文件进行解析的。非相对导入:编译器会从包含导入文件的目录开始依次向上级目录遍历,尝试定位匹配的声明文件。
相关面试题
type 和 interface的区别
用interface来描述数据结构,用type描述类型,
相同点:
- 都可以用来描述函数和对象
- 都允许extends(继承) 不同点:
- type 可以使用联合类型
- type 可以指定数组的位置
什么是泛型,泛型的具体使用?
如何基于一个已有的类型,扩展出一个大部分内容相似,但是部分区分的类型
可以用Pike或Omit、Partial或Required