Typescript 的类型

391 阅读6分钟

Typescript

类型

String string 区别

const msg: string = 'hello'; // 一般都写小写就ok了
const message: String = 'hello';

// 这个就代表这个文件是独立的模块, 就不会报在其他文件里重名的错误了
export {};

这边冒号后面的类型是区分开来的

  • strinig: 代表 ts 中的字符串类型
  • String: 代表 js 中字符串包装类的类型
类型推导
let foo = 'cqc'; // 在默认情况下赋值, ts会自动将赋值的类型作为签名标识符的类型
foo = 123456; // 这里会报错  Type 'number' is not assignable to type 'string'.

数据类型 ( js 中存在的 )

默认情况下如果可以推导出(infer)标识符的类型时候 一般不手动加类型

数值 number
//? number
// ts 数值类型不区分 int float
let num1: number = 213; // 普通数值
let num2: number = 0b111; // 二进制
let num3: number = 0o456; // 八进制
let num4: number = 0x9ac; // 十六进制
布尔 boolean
// ? boolean
let flag1 = 2 > 3;
let flag2 = false;
字符 string
数组 Array
// 一个数据中存放的元素类型应该是固定的
// 注意这里是Array 不是 array
const arr1: Array<string> = ['cqc']; // 不推荐(jsx 中冲突)
const arr2: string[] = ['cqc']; // 推荐 效果同arr1
对象 object
// 可以赋默认值 自动推导
const info1 = {
  name: 'cqc',
  age: 24,
};

// 十分不推荐(使用info2.name的时候不会自动弹出提示 且报错 Property 'name' does not exist on type )
const info2: object = {
  name: 'cqc',
};
空值 null
// null 类型只有一个 null 的值 只能赋值 null
const nl: null = null; // null 类型时候最好手动指定下类型(否则类型自动推导是 any)
未定义 undefined
// 和 null 类型一样 只有一个值 只能赋值 undefined
const und: undefined = undefined;
唯一 symbol

数据类型 ( ts 中独有的 )

any
  • 应用场景 当进行一些类型断言的时候 as any 否则报错 在不想给某些 js 添加具体数据类型时候 any-script?

    const arr: any[] = [];
    
unknown

类似 any 类型, 用于不确定类型 在不能推导出来的时候使用 (可能是类型 a 可能是类型 b)

unknown、any 区别

unknown 类型的数据只能赋值给 unknown 类型 或者 any 类型, 但是 any 类型可以赋值任意类型

let unknown1: unknown;
let any1: any;

const str1: string = any1;
// ok

const str2: string = unknown1;
// Type 'unknown' is not assignable to type 'string'
void
// 指定一个函数返回值类型 只能是 null || undefined
const foo = function (str: string): void {
  return null || undefined;
};
never

表示一个函数永远不会发生值的类型

应用场景

const foo = function (): never {
  // while( true ) {};
  throw new Error();
};
const handler = function (message: string | number) {
  switch (typeof message) {
    case 'string':
      ...
      break;
    case 'number':
      ...
      break;

    default:
      // 函数进入这里就会报错
      const check: never = message;
      break;
  }
};

// 这时候加入有人需要用 handler 函数处理其他类型, 那么他有可能在联合类型内加入他要处理的类型 比如:
const handler = function (message: string | number | boolean) {...};

// 这时候 handler 函数内没有处理到 boolean 这个新类型, switch 进入到 default 处理方式, 直接编译报错, 提醒人添加新类型的处理方式

元组 tuple

多种元素组合而成的数组

const tupleInfo: [string, number, number] = ['cqc', 1.7, 170];

// 应用场景
// 希望调用一个函数 得到 [counter, setCounter]
type MyFunction = <T>(state: T) => [T, (newValue: T) => void];
const useState: MyFunction = function useState<T>(state: T) {
  let currentState = state;

  const changeState = (newState: T) => {
    currentState = newState;
  };

  return [currentState, changeState];
};

const [counter, setCounter] = useState(10);
枚举
// 默认值 从 0 开始
enum Direction {
  LEFT, // 0
  RIGHT, // 1
  TOP, // 2
  BOTTOM,
}

function turnDirection(direction: Direction) {
  switch (direction) {
    case Direction.LEFT:
      // ...
      break;
    case Direction.RIGHT:
      // ...
      break;
    // ...
    default:
      break;
  }
}
turnDirection(Direction.RIGHT);

// 可以自定义值
enum DirectionMy {
  TOP = 10,
  BOTTOM = 20,
  LEFT, // 21 会取上面那个 + 1
}

let d: DirectionMy = DirectionMy.TOP;

enum DirectionMy1 {
  TOP = 'TOP', // 只可以是字符串
}
泛型(重要)
const foo = function <T, S>(typeT: T, typeS: S) {
  const tupleArr: [T, S] = [typeT, typeS];
  return tupleArr;
};

// 接口中使用泛型
interface IPerson<T1, T2 = number> {
  // T2 使用了默认类型
  name: T1;
  age: T2;
}

const p: IPerson<string, number> = {
  name: 'cqc',
  age: 23,
};

// 类中使用泛型
class Point<T> {
  x: T;
  y: T;

  constructor(x: T, y: T) {
    this.x = x;
    this.y = y;
  }
}

const p = new Point('1.33', '2.22');
const p1 = new Point<string>('1.33', '2.22');
const p2: Point<string> = new Point('1.33', '2.22');

// 普通数组中使用泛型
const names: Array<string> = ['cqc']; // 不推荐
泛型的限制
interface ILength {
  length: number;
}

interface IMy {
  name: string;
}
const getLength: <T extends ILength & IMy>(arg: T) => number = function (arg) {
  return arg.length;
};

getLength({ length: 3, name: 'cqc' });
函数参数和返回值类型
function sum1(n1: number, n2: number): number {
  return n1 + n2;
}

const names = ['cqc', 'why', 'plo'];
//匿名函数 item根据上下文的环境推导出来,可以不写注解
names.forEach((item) => {
  item.length; // item: string
});

// 定义常量时候
type AddFnType = (...args: number[]) => number;
const sum: AddFnType = (...args: number[]) => {
  return args.reduce((p, c) => p + c);
};

sum(1, 2, 3, 1);
对象类型
type PrintType = {
  x: number;
  y: number;
  z?: number; // 可选类型  效果和 z: number | undefined 一样
};
function printPoint(point: PrintType) {
  console.log(point.x);
  console.log(point.y);
  console.log(point.z);
}
联合类型

使用联合类型值时候, 需要小心该值的类型 缺点:

  1. 进行很多逻辑判断(类型缩小)
  2. 返回值类型不能确定
function printID(id: number | string) {
  id;
}

// 让传入的参数是可选的
function foo(name?: string) {}
类型别名
type IDType = string | number | boolean;
类型断言 as

类型断言只允许转换为 更具体 或 不太具体的 类型, 防止不可能的强制转换

// ? 类型断言 as
/* 
  默认这种方式获取的el 类型是 HTMLElement
  但是不同html标签属性不一样
  这时候可以使用类型断言
*/
const el = document.getElementById('img') as HTMLImageElement;
el.src; // 在不使用类型断言时候 Property 'src' does not exist on type 'HTMLElement'

// ---------

class Person {}
class Student extends Person {
  study() {}
}
function satHi(p: Person) {
  // 这里知道p是 Student, 直接断言
  (p as Student).study();
}
const stu = new Student();
satHi(stu);

// ----------
// 不是特殊情况不推荐
const message = 'hello';
const num: number = message as number; // 报错
const num: number = message as unknown as number; // 需要先断言 known 在断言 number, 才能赋值
非空类型断言
function printMessage(message?: string) {
  message!.toUpperCase(); // 这里加 ! 告诉ts编译器跳过对这部分的检查  但是调用该方法不传递参数 该报错还报错 printMessage()
}
字面量类型

字面量类型与他的值保持一致

const message: 'cqc' = 'cqc';

// 字面量类型的意义, 就是结合 联合类型 (和枚举类似)
type Align = 'left' | 'center' | 'right';
let align: Align = 'center';

// ------------
// 字面量推理
type Method = 'POST' | 'GET';
function request(url, method: Method) {}

// 一般建议直接给 options 定死类型
const option = {
  url: 'https:/.xxx/a',
  method: 'POST' as const, // 不加这个的话, 在调用 request 方法时候 这个属性类型会被定义 string, 加了后类型就是 POST
};

request(options.url, options.method);

操作符

  • ?? 空值合并操作符
const a = false;
const b = 'cqc';
const res = a ?? b;

// 当 a 为 null || undefined 时候, 返回右边值, 否则返回左侧值

类型缩小

  • 常见类型缩小方式
    • typeof
    • 平等缩小 ( === , !== )
    • instanceof // 实例(==new 出来的==) instanceof 原型
    • in
    • ...
let a = xxx;
// 缩小类型范围
if(typeof a === 'string') {
  ...
}

switch(..) {
  ...
}


function printTime(time: string | Date) {
  if (time instanceof Date) {
    time.getTime()
  }
}

// instanceof -------------
class Student {
  studing() {}
}

class Teacher {
  teaching() {}
}

function work(p: Student | Teacher) {
  if (p instanceof Student) {
    ...
  } else {
    ...
  }
}

函数重载

函数名称相同,但是参数不同的几个函数 如果可以通过联合类型实现的函数重载, 直接联合类型 感觉函数重载比较鸡肋,最终函数逻辑的实现还是在一个函数体内去判断它的参数类型,然后做相应的操作。==ts 重载的作用,只是多了一个参数校验的功能==。

// 编写一个add函数, 可以数字之间相加、也可以数字、字符串
type AddType = number | string;
function add(a1: number, a2: number): number; // 没有函数体
function add(a1: string, a2: string): number;

function add(n1: any, n2: any): any {
  if (typeof n1 === 'string' && typeof n2 === 'string') {
    return n1.length + n2.length;
  }
  return n1 + n2;
}
const res = add('20', '5');
// 在函数重载中, 实现函数是不能直接被调用的
// add([], 123); //No overload matches this call.