TS

452 阅读7分钟

课程目标

  • TS

知识点

  • TS语法及用法

参考

TypeScript中文官网地址: www.tslang.cn/

TypeScript

  • TypeScriptJavaScript的超集,支持ECMAScript6标准
  • TypeScript是由微软开发的自由和开源的编辑语言
  • 可以编译成纯javascript,编译出来的javascript可以运行在任何浏览器上

TypeScript和JavaScript的区别?

  1. TypeScriptJavaScript的超集,扩展了JavaScript的语法,现在有JavaScript代码可与TypeScript一起工作,TypeScript通过类型注解提供编译时的静态类型检查
  2. TypeScript可处理已有的JavaScript代码,只对其中的TypeScript代码进行编译
  3. ts在开发时就能给编译错误,js错误只能在运行时体现。
  4. 作为强类型的语言,可以明确知道所有数据的类型。
  5. ts是面向对象的语言,包含了类、接口的概念。

安装TypeScript

npm install -g typescript
  1. 安装成功后可以使用tsc命令来执行TypeScript的相关代码,比如查看版本号:
tsc -v
  1. 创建一个ts文件,以.ts为后缀名,比如app.ts
  2. TypeScript转换为JavaScript,命令行:
tsc app.ts
  1. 使用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接口

  1. 可选属性:接口里的字段不全都是必须,在字段名字定义的后面加?符号
  • 可选属性的好处:可以对可能存在的属性进行预定义,可以捕获引用了不存在的属性时的错误
  1. 只读属性:一些对象属性只能在对象刚刚 创建的时候个性其值,可以在属性名前用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',因为是只读属性
  1. 接口继承:接口可以通过其他接口来扩展自己。允许接口继承多个接口,使用关键字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模块

  • 模块在其自身的作用域里执行,而不是在全局作用域里。
  • 两个模块之间的关系是通过在文件级别上使用importexport建立的。
  • 任何包含顶级import或者export的文件都会被当成一个模块,如果一个文件不带项级的import或者export声明,会被视为全局。
  1. 导出 任何声明(比如变量、函数、类、类型别名或接口)都可以通过添加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'
  1. 导入 用关键字import来导入
import { ZipValidator } from 'ZipValidator'
let val = new ZipValidator();
  1. 模块相关配置项:module
  • ESNext: 代表ES最新版本
  • UMD/AMD
  • ES2015
  • ES2020
  1. 模块相关配置项:moduleResolution模块解析,一般用node,但node不是默认项。
  • node:
  • classic:
  1. 模块解析 模块解析是指编译器在查找导入模块内容时所遵循的流程
  • 首先编译器会尝试定位表示导入模块的文件,会遵循二种策略:classicnode,这些策略会告诉编译器到哪里找module.
  • 如果解析失败并且模块名是非相对的,编译器会尝试定位一个外部模块声明

根据模块引用是相对的还是非相对的,模块导入会以不同的方式解析。

  • 相对导入:是相对于导入它的文件进行解析的。
  • 非相对导入:编译器会从包含导入文件的目录开始依次向上级目录遍历,尝试定位匹配的声明文件。

相关面试题

type 和 interface的区别

用interface来描述数据结构,用type描述类型,

相同点:

  • 都可以用来描述函数和对象
  • 都允许extends(继承) 不同点:
  • type 可以使用联合类型
  • type 可以指定数组的位置

什么是泛型,泛型的具体使用?

如何基于一个已有的类型,扩展出一个大部分内容相似,但是部分区分的类型

可以用PikeOmitPartialRequired