TS入门

123 阅读8分钟

TS

// 全局安装命令
npm install typescript -g

// 查看版本
tsc --version
方式一:通过webpack,配置本地的TypeScript编译环境和开启一个本地服务,可以直接运行在浏览器上;

方式二:通过ts-node库,为TypeScript的运行提供执行环境;

使用ts-node

//安装ts-node
npm install ts-node -g

//另外ts-node需要依赖 tslib 和 @types/node 两个包: 
npm install tslib @types/node -g

//现在,我们可以直接通过 ts-node 来运行TypeScript的代码:
ts-node math.ts

使用webpack

// 安装webpack webpack-cli
npm install webpack webpack-cli -D //package.json中"build": "webpack"
// 安装ts-loader typescript
npm install ts-loader typescript -D
// 安装tsconfig.json
tsc init
// 安装webpack-dev-server搭建本地服务
npm i webpack-dev-server -D //package.json中"serve": "webpack serve"
// tslint校验
npm install tslint -g

变量的类型推导

let foo = '123'
foo = 123 // 报错 因为foo赋值的是字符串所以推导到foo只能用字符串类型

TS类型运用

// number
let num: number = 100
let num1: number = 0b100
let num2: number = 0o456
let num3: number = 0x234

console.log(num, num1, num2, num3)

// boolean
let flag: boolean = true
flag = false
flag = 20 > 40

console.log(flag)

// string
let str: string = 'hello World'
str = '123'

// 数组
const names: Array<string> = ['123', 'hello'] // 不推荐 (react jsx中冲突)
const names1: string[] = ['123', 'hello'] // 推荐


// 对象 能推导直接推导
// const info: object = {
//   name: 'aaa',
//   age: 20
// }

// symbol
// @ts-ignore
const s1: symbol = Symbol('title')
// @ts-ignore
const s2: symbol = Symbol('title')

// null undefined
let n1: null = null // 只能赋值给null
let u1: undefined = undefined // 只能是undefined

// any 可以赋值给任意类型
let anyStr: any = 123
anyStr = '123'
console.log(anyStr)

let anyArray: any[] = [1,'233']
console.log(anyArray)

// unknow 只能赋值给any和unknown类型
let unknownStr: unknown = 123
unknownStr = '123'
console.log(unknownStr)

// let message: string = unknownStr // 报错只能赋值给any和unknown类型

// void类型
function sum(num1: number, num2: number):void {
  console.log(num1 + num2)
}

// never类型 永远不会返回结果
function handleMessage() : never {
  while (true) {
    console.log('1111')
  }
}

// tuple元组类型
const tuple1: [string, number, number] = ['why', 18, 1.88]
console.log(tuple1)

const tuple2: (string | number | boolean)[] = ['mov', 20]
console.log(tuple2)
const name = tuple2[0]

// 函数参数是对象类型
// 可选类型 ?
// 联合类型 |
function printPoint(point: {x: number, y?: string | number}) {
  console.log(point.x)
  console.log(point.y)
}

printPoint({x: 123})

// 可选类型
function printPoint1(point: {x: number, y?: string | number}) {
  console.log(point.x)
  console.log(point.y)
}
//相当于
function printPoint2(point: {x: number, y: string | number | undefined}) {
  console.log(point.x)
  console.log(point.y)
}

// 类型别名
type Point = {
  x: number,
  y: number
}
type message = string | number | boolean
function printPoint3(point: Point) {
  console.log(point.x)
  console.log(point.y)
}
function printID(id: message) {
  
}

// 类型断言as 转化为更具体的类型或者更广泛的类型
const el = document.getElementById('my-img') as HTMLImageElement
el.src = ''

class Person {
}

class Student extends Person {
  constructor() {
    super();
  }
  studying() {
  }
}
function sayHello(p: Person) {
  (p as Student).studying()
}
const stu = new Student()
sayHello(stu)

类型缩小

type TypeId = number | string
function printId(id: TypeId) {
  if (typeof id === 'number') {
    console.log(id)
  } else {
    console.log(id.toUpperCase())
  }
}

// in
type Fish = {
  swim: () => void
}

type Dog = {
  run: () => void
}

function walk(animal: Fish | Dog) {
  if ('swim' in animal) {

  } else {

  }
}

const fish: Fish = {
  swim: function () {
    console.log('swimming')
  }
}

walk(fish)

函数类型

function foo(num1: number, num2: number) {

}

// 函数类型
type Foo = (num1: number, num2: number) => void

function bar(fn: Foo) {
  fn(20, 30)
}

bar(foo)

// 字面量定义
type addType = (num1: number, num2: number) => number
const add: addType = (num1: number, num2: number) => {
  return num1 + num2
}

// 如果是元组
const arr: number[] = [1, 2]
const tuple1: [string, (num1: number, num2: number) => void] = ['hello', function bar(num1, num2) {
}]

// 函数剩余参数
function restParamter(...nums: number[]) {
  console.log(nums)
}

restParamter(123, 222)

// this的类型
// 当this的类型不明确的时候,ts需要明确指定this的类型
type thisType = {
  name: string
}

function eating(this: thisType) {
  console.log(this.name, 'name')
}

const info = {
  name: 'why',
  eating: eating
}

info.eating()
eating.call({name: 'Kobe'})

函数的重载

// 函数的重载:函数名称相同,但是函数参数不同(参数个数不同,参数类型不同)的几个函数就是函数的重载
function add(num1: number, num2: number):number
function add(num1: string, num2: string):string
function add(num1: any, num2: any): any {
  return num1 + num2
}

const result = add(10, 20)
console.log(result)

联合声明和重载

// 联合声明
// function getLength(args: string | any[]) {
//     return args.length
// }
//
// console.log(getLength('asfadsfds'));

// 函数重载
function getLength(args: string): number
function getLength(args: any[]): number
function getLength(args:any):any {
    return args.length
}

console.log(getLength([1, 2, 3, 4]));
console.log(getLength('1w123123'));

类的使用

// 类的基础使用
class Person {
    name: string
    age: number

    constructor(name: string, age: number) {
        this.name = name
        this.age = age
    }

    eating() {
        console.log('eating')
    }
}

class Student extends Person{
    sno: number
    constructor(name: string, age: number, sno: number) {
        super(name, age);
        this.sno = sno
    }
    studying() {
        console.log('student studying')
    }
    eating() {
        super.eating();
        console.log('student studying')
    }
}

const stu = new Student('why', 20, 100)
stu.eating()

// 类修饰符
// public 修饰的是在任何地方可见、公有的属性或方法,默认编写的属性就是public的;
// private 修饰的是仅在同一类中可见、私有的属性或方法;
// protected 修饰的是仅在类自身及子类中可见、受保护的属性或方法;

class Person {
    protected name: string = 'why'
    getName() {
        return this.name
    }
}

class Student extends Person {
    getName() {
        return this.name
    }
}

const stu = new Student()
const p = new Person()
console.log(p.getName());

// console.log(stu.getName())



readonly修饰符

// readonly修饰符
// 1.只读属性可以在构造器中赋值,但是不能被修改
// 2.属性本身不能被修改,但是如果属性是对象的话,对象中的属性是可以被修改的
class Person {
    readonly name: string
    readonly friend?: Person
    constructor(name:string, friend?:Person) {
        this.name = name
    }
}

const p = new Person('why', new Person('Kobe'))
console.log(p)

访问器

class Person {
    private _name: string
    static age: number = 1234

    constructor(name: string) {
        this._name = name
    }

    get name() {
        return this._name
    }

    set name(newName) {
        this._name = newName
    }
    static addStudent() {
        console.log('addStudent')
    }
}

const p = new Person('why')
console.log(p.name);
p.name = '123'
console.log(p.name)
console.log(Person.age);
Person.addStudent()

抽象类

// 继承是多态的前提
// 因为new Shape中getArea没有实现,所以需要使用抽象类
// 抽象类中的方法可以不需要实现,但是子类中必须实现

// 所以在定义很多通用的调用接口时, // 我们通常会让调用者传入父类,通过多态来实现更加灵活的调用方式。
// 但是,父类本身可能并不需要对某些方法进行具体的实现,所以父类中定义的方法,// ,我们可以定义为抽象方法。
// 什么是 抽象方法? 在TypeScript中没有具体实现的方法(没有方法体),就是抽象方法。
// 抽象方法,必须存在于抽象类中;
// 抽象类是使用abstract声明的类;
// 抽象类有如下的特点:
// 抽象类是不能被实例的话(也就是不能通过new创建) 
// 抽象方法必须被子类实现,否则该类必须是一个抽象类;

abstract class Shape {
    abstract getArea():number
}

class Rectangle extends Shape {
    private readonly width: number
    private readonly height: number

    constructor(width: number, height: number) {
        super();
        this.width = width
        this.height = height
    }

    getArea() {
        return this.width * this.height
    }
}

class Circle extends Shape {
    private readonly r: number

    constructor(r: number) {
        super();
        this.r = r
    }

    getArea() {
        return Math.PI * (this.r ** 2)
    }
}

// 多态:不同的类型调用同一个方法,表现不同就是多态
function makeArea(shape:Shape) {
    return shape.getArea()
}

console.log(makeArea(new Rectangle(10, 20)));
console.log(makeArea(new Circle(10)));

类的类型

// 把一个类当做类型

class Person {
    name:string = 'why'
    eat: () => void
}

const p: Person = {
    name: 'why',
    eat: () => {
        console.log('eat')
    }
}

接口类型

type info = {
    name: string,
    age: number
}

interface IInfo {
    readonly name?:string,
    age: number,
    getName?: () => void
}

const obj:IInfo = {
    name: 'why',
    age: 20
}

obj.age = 3

索引类型和函数类型

// 索引类型
interface IIndexPosition {
    [index: number]: string
}

const indexType: IIndexPosition = {
    0: 'jAVA',
    1: 'HTML',
    2: 'CSS'
}

console.log(indexType)

// 函数类型
// type Fn = (num1: number, num2: number) => number
interface ICalc {
    (num1: number, num2: number): number
}
function foo(num1: number, num2: number, calc: ICalc) {
    return calc(num1, num2)
}

console.log(foo(10, 20, (num1, num2) => {
    return num1 + num2
}));


export {}


接口继承和交叉类型

// type Fn = () => any

// 接口继承
interface ISwim {
    swim: () => void
}

interface IFly {
    fly: () => void
}

interface IAction extends ISwim, IFly {
    play: () => void
}

// 支持多继承
const actions: IAction = {
    play: () => {

    },
    swim: () => {

    },
    fly: function () {

    }
}

//交叉类型
interface IColor {
    color: string
}

interface IFn {
    run: () => void
}
// 交叉类型
type MyType = IColor | IFn
type IntertSect = IColor & IFn

const obj: IntertSect = {
    color: 'red',
    run: () => {

    }
}

interface和type的区别

// interface 可以重复的对某个接口来定义属性和方法;
// 而type定义的是别名,别名是不能重复的;

接口的实现

interface ISwim {
    swim: () => void,
    name:string
}

interface IFly {
    fly: () => void
}

// 类是单继承的
// 类可以实现多个接口
class Animal {

}

class Fish extends Animal implements ISwim, IFly {
    name:string
    constructor(name) {
        super();
        this.name = name
    }
    swim() {

    }

    fly() {

    }
}

class Dog implements ISwim {
    name: string = '123'
    swim() {

    }
}

function swimAction(swim: ISwim) {
    swim.swim()
}

swimAction({
    swim: () => {
        console.log('游泳')
    },
    name: '123'
})

freshness的擦除操作

interface IPerson {
    name: string,
    age: number,
    height: number
}

// 进行对象引用赋值的时候会进行freshness擦除操作
// 将多余的属性擦除后再和接口进行类型判断是否符合类型条件

const info = {
    name: 'why',
    age: 18,
    height: 1.88,
    address: '杭州市'
}

const p: IPerson = info

枚举类型的使用

// 枚举类型就是一组常量
enum Direction {
    LEFT,
    RIGHT,
    TOP,
    BOTTOM
}

let d: Direction = 12
console.log(d)

function turnDirection(direction: Direction) {
    switch (direction) {
        case Direction.LEFT:
            console.log('转向左边')
            break
        case Direction.RIGHT:
            console.log('转向右边')
            break
        case Direction.BOTTOM:
            break
        case Direction.TOP:
            break
        default:
            const foo: never = direction
            break
    }
}

泛型的基本使用

// 泛型的基本使用
// 参数类型化
function foo<T>(num1: T) {
   return num1
}

console.log(foo<object>({name: 'why'}));

// 泛型可以接收多个类型

function bar<T, E>(num1: T, num2: E) {
    console.log(num1, num2)
}

bar<number, string>(123, 'abc')

泛型接口的使用

// 泛型接口的使用
interface IPerson<T1, T2> {
    name:T1,
    age: T2,
    getName?: () => void
}

const p:IPerson<string, number> = {
    name: 'why',
    age: 123
}


// 泛型类的使用
class Person<T, E> {
    name:T
    age: E
    constructor(name: T, age: E) {
        this.name = name
        this.age = age
    }
}

const p1 = new Person<string, number>('why', 10)

// 泛型的类型约束
interface ILength {
    length: number
}
function getLength<T extends ILength>(arg: T) {
    return arg.length
}

getLength('123')

外部声明和自定义声明

//方式一:在自己库中进行类型声明(编写.d.ts文件),比如axios
//方式二:通过社区的一个公有库DefinitelyTyped存放类型声明文件 //该库的GitHub地址:https://github.com/DefinitelyTyped/DefinitelyTyped/
//该库查找声明安装方式的地址:https://www.typescriptlang.org/dt/search?search=


// 否则自己写声明 lodash.d.ts
//声明模块的语法: declare module '模块名' {}。
//在声明模块的内部,我们可以通过 export 导出对应库的类、函数等;
declare module 'lodash' {
  export function join(args: any[]):void
}

// 声明变量
declare let name: string
declare let age: number
declare let height: number

declare function whyoo():void
declare class Person {
  name:string
  number: number
  constructor(name:string, age: number)
}

// 声明文件
declare module '*.jpg'
declare module '*.svg'
declare module '*.jpeg'
declare module '*.png'
declare module '*.gif'

declare module '*.vue' {
  
}

image-20220105111007650

image-20220105111154444

image-20220105111204473