typescript基础学习

102 阅读5分钟

1.数据类型

1.基础数据类型

ts不能被浏览器理解,需要编译成js,可以理解是js的超集

注意数据类型都是小写的

boolean

number

string

undefined

null

默认情况下null和undefined是所有类型的子类型,可以把null和undefined赋值给number等其他类型的变量

let age: number = null;
let name: string = undefined;

any

不清楚用什么类型,可以使用 any 类型,意思就是啥都可以,不建议

unknown

代表任何类型,是一个安全类型,推荐,可以配置断言使用

function divide(param: unknown) {
  return param as number / 2;
}

void

标识没有任何返回值,这个应该很熟悉

2.数组/元组/对象/函数

数组类型

let list: number[] = [1,2,3]; // 不限定数量
list.push(4);

元组类型

元组类型表示已知元素数量和类型的数组

let arr: [number, string] = [20, 'yuping'];

可以使用push方法,但是只能是指定的类型

函数类型

定义函数类型需要定义输入参数类型和输出类型,使用时注意:

参数后加个问号,代表这个参数是可选的;

可选参数要放在函数入参的最后;

可以在入参里面定义初始值;

ts中函数不可以直接复制,举个栗子:

let add1 = (x: number, y: number): number => {
  return x + y;
}
let add2: (x: number, y: number) => number = add1; // 即要定义变量的类型

函数重载:函数名称相同,参数个数或者类型不同,一般用泛型解决

interface类型,可以用来定义对象或者数组

定义interface一般首字母大写

interface Person {
  readonly id: number
  name?: string
  age: number
}

interface Sum {
  (x: number, y: number): number
}
const mySum: Sum = (x, y) => {
  return x + y;
}

假如一个对象上面有很多不确定的属性,可以自定义属性

interface RandomKey {
  [propName: string]: string
}
const instance: RandomKey = {
  age: '20',
  name: 'yuping',
};

interface likeArr {
  [propName: number]: string
}
const arr: likeArr = ['20', 'yuping'];

因为interface很灵活,所以它有个响亮的名字叫鸭子类型(duck typing)

3.面向对象部分

1.public private protected static

public共有的

private私有的,只在类的内部可访问,实例和子类不能访问

protected保护的,类的内部和子类内部可以访问,实例不能访问

static静态的,只能通过类名访问

2.abstract抽象类

只能被继承,不能实例化的类就叫做抽象类,抽象类中的抽象方法必须被继承的子类实现

abstract class Animal {
  construct(name: string) {
    this.name = name;
  }
  name: string
  abstract sayHi(): void
}
class Dog extends Animal {
  construct(name: string) {
    super(name);
  }
  sayHi() {
    console.log('hello');
  }
}

抽象类的用法是用来定义一个基类,声明共有属性和方法,拿去被继承,有利于实现多态

所谓多态就是父类定义的一个抽象方法,在子类中有不同的实现

3.implements实现

有时候具有相同功能的类并不具有相同的父类,所以通过抽象类继承的方式就会有问题

interface原本是设计用来定义对象类型的,但是也可以用来约束class

interface MusicInterface {
  playMusic(): void
}
class CellPhone implements MusicInterface {
  playMusic() {
    console.log('playing music');
  }
}

implements只能约束类实例上的属性和方法,要约束构造函数和静态属性,需要这么写

interface CircleStatic {
  new (radius: number): void
  pi: number
}
const Cirlce: CircleStatic = class Cirlce {
  construct(radius: number) {
    this.radius = radius;
  }
  radius: number
  static pi: 3.14
}

4.枚举类型enum

相当于创建了一个code到name的映射关系

enum Direction {
  left = '100',
  right = 20,
  up = 15,
  down = '23',
}
console.log(Direction.left); // '100'
console.log(Direction.right); // 20
console.log(Direction.up); // 15
console.log(Direction.down); // '23'
console.log(Direction['100']); // left
console.log(Direction[20]); // right
console.log(Direction[15]); // up
console.log(Direction['23']); // down

5.内置类型

Array Date Error RegExp HTMLElement NodeList MouseEvent等等

2.进阶内容

1.联合类型 交叉类型 type类型别名

联合类型 |

let num: string | number

当TS不确定一个联合类型的变量到底是哪个类型的时候,只能访问他们共有的属性和方法

交叉类型 &

interface Person {
  name: string
  age: number
}
type Student = Person & { grade: number }

类型别名type:类似于用var定义变量

type Name = string;
type NameResolver = () => string;
type NameOrResolver = Name | NameResolver;

type和interface的区别

interface使用extends实现继承, type使用交叉类型实现继承

interface的声明会被合并,但是type不会

2.类型断言as

使用类型断言来告诉TS,我(开发者)比你(编译器)更清楚这个参数是什么类型,你就别给我报错了,举个栗子

function getLength(arg: number | string): number {
  const str = arg as string;
  if (str.length) {
    return str.length;
  } else {
    const number = arg as number;
    return number.toString().length;
  }
}

注意类型断言不是类型转换

3.字面量(常量)类型

type ButtonSize = 'mini' | 'small' | 'normal' | 'large';
const myButtonSize = 'small';

4.泛型<>

可以用来保持输入输出的一致性,可以理解是在定义时候类型的一个占位符,使用的时候把实际的类型传递进去(这不就是参数传值吗),一看到<>就知道是泛型。下面是泛型的很多栗子

处理多个函数参数

function swap<T, U>(tuple: [T, U]): [U, T] {
  return [tuple[1], tuple[0]];
}

希望调用api可以清晰的知道返回的数据结构

interface UserInfo {
  name: string
  age: number
}
function request<T>(url:string): Promise<T> {
  return fetch(url).then(res => res.json());
}
request<UserInfo>('user/info').then(res => {
  console.log(res);
})

对于泛型进行约束,通过继承实现

interface ILength {
  length: number
}
function printLength<T extends ILength>(arg: T): T {
  console.log(arg.length);
  return arg;
}

泛型约束数组

const arr: Array<number> = [1,2,3];

泛型约束class

class Stack<T> {
  private data: T[] = []
  push(item: T) {
    return this.data.push(item);
  }
  pop(): T | undefined {
    return this.data.pop();
  }
}
const s1 = new Stack<number>();

泛型约束interface

interface IKeyValue<T, U> {
  key: T
  value: U
}
const k1: IKeyValue<number, string> = { key: 18, value: 'lin' };
const k2: IKeyValue<string, number> = { key: 'lin', value: 18 };

3.ts结合vue3

申明类型的两种方式:运行时申明和基于类型的申明

1.props

基于类型的声明props需要使用withDefaults设置默认值

export interface Props {
  msg?: string
  labels?: string[]
}
const prosp = withDefaults(defineProps<Props>(), {
  msg: 'hello',
  labels: () => ['one', 'two']
})

2.emits

<script setup lang="ts">
// 运行时
const emit = defineEmits(['change', 'update']);

// 基于类型
const emit = defineEmits<{
  (e: 'change', id: number): void
  (e: 'update', value: string): void
}>();
</script>

3.ref

const year = ref<string | number>('2020');

year.value = 2020; // 成功!

4.reactive

import { reactive } from 'vue';

interface Book {
  title: string
  year?: number
}

const book: Book = reactive({ title: 'Vue 3 指引' });

5.computed

const double = computed<number>(() => {
  // 若返回值不是 number 类型则会报错
});

6.事件处理函数

官方栗子

<script setup lang="ts">
function handleChange(event: Event) {
  console.log((event.target as HTMLInputElement).value);
}
</script>

<template>
  <input type="text" @change="handleChange" />
</template>

7.provide/inject

通过symbol注入,需要配合InjectionKey

import { provide, inject } from 'vue';
import type { InjectionKey } from 'vue';

const key = Symbol() as InjectionKey<string>;

provide(key, 'foo'); // 若提供的是非字符串值会导致错误

const foo = inject(key); // foo 的类型:string | undefined

通过字符串注入,带默认值

const foo = inject<string>('foo', 'bar'); // 类型:string | undefined

8.模板引用

<script setup lang="ts">
import { ref, onMounted } from 'vue';

const el = ref<HTMLInputElement | null>(null);

onMounted(() => {
  el.value?.focus();
})
</script>

<template>
  <input ref="el" />
</template>

9.组件模板引用

<!-- MyModal.vue -->
<script setup lang="ts">
import { ref } from 'vue';

const isContentShown = ref(false);
const open = () => (isContentShown.value = true);

defineExpose({
  open,
})
</script>

<!-- App.vue -->
<script setup lang="ts">
import MyModal from './MyModal.vue';

const modal = ref<InstanceType<typeof MyModal> | null>(null);

const openModal = () => {
  modal.value?.open();
}
</script>

typeof + InstanceType