TypeScript基础

44 阅读5分钟

1. 八种基本类型

string、number、boolean、undefined、null、object、bigint、symbol

与JavaScript相同

let str: string = "jimmy";
let num: number = 24;
let bool: boolean = false;
let u: undefined = undefined;
let n: null = null;
let obj: object = {x: 1};
let big: bigint = 100n;
let sym: symbol = Symbol("me"); 

1.1. 注意点:

  1. null和undefined可以赋值给任何类型,即null和undefined是任何类型的子类型。
  2. number和bigint都表示数字,但是这两种类型并不兼容。

2. 其他特殊类型

2.1. 数组

数组的定义方法:

const list1: number[] = [1, 2, 3];
// 泛型的定义方法
const list2: Array<number> = [1,2,3];
// 数组元素的具体类型定义方法
const list3: [number, string, boolean] = [1, '2', true];
// 数组对象的对象方法
const list4: [{name: string, age: number}] = [{name: 'jack', age: 18}];
// 采用泛型的定义方法
const list5: Array<{name: string, age: number}> = [{name: 'jack', age: 30}];
// 接口的定义方法,对象中的属性过多的时候可采用
interface User{
  name: string,
  age: number
}
const list6: Array<User> = [{name: 'jack', age: 30}];

export default {}

2.2. 函数

函数的定义方法:

// 函数声明式
function sum(x: number, y: number): number {
    return x + y;
}
// 函数表达式
const sum = (x:number, y:number): number=>{
  return x + y;
}
// 函数表达式另一种写法 斜括号看作一个整体
const sum:/(x: number, y:number) => number/ = (x, y) =>{
  return x + y;
}
// 参数可选 参数默认值 剩余参数
1. function buildName(firstName: string, lastName?: string){}
2. function buildName(firstName: string, lastName: string = 'Cat'){}
3. function push(array: any[], ...items: any[]){}

2.3. void

void表示没有任何类型,不能直接赋值。

let a: void; 
let b: number = a; // 报错

2.4. never

never表示用不存在的值的类型。

值永不存在的两种情况:

// 异常
function err(msg: string): never { // OK
  throw new Error(msg); 
}

// 死循环
function loopForever(): never { // OK
  while (true) {};
}

实践使用:never可以用于避免出现新增了联合类型,提示报错,保证函数返回值的类型控制在正确的范围内。

type Foo = string | number;

function controlFlowAnalysisWithNever(foo: Foo) {
  if (typeof foo === "string") {
    // 这里 foo 被收窄为 string 类型
  } else if (typeof foo === "number") {
    // 这里 foo 被收窄为 number 类型
  } else {
    // foo 在这里是 never
    const check: never = foo;
  }
}

2.5. any

any类型是任何类型的父类型,any类型允许被赋值任何类型。

但是any类型不太安全,不太推荐使用该类型。

let a: any = 666;
a = "Semlinker";
a = false;
a = 66
a = undefined
a = null
a = []
a = {}

2.6. unknow

unknown与any一样,所有类型都可以分配给unknown,反之把unknown 赋值给其它类型会报错。

// unknown 可以接收任意类型
let name:string = "jack"
let user:unknown = name;

// unknown 不可以赋值给其它类型,any除外,下面会报错
let name:unknown = "jack"
let user:string = name;

3. 交叉类型

交叉类型通常用于多个接口合并成一个类型,从而实现等同接口继承的效果,也就是合并接口类型。

type User = { name: string }
type AgeType = { age: number }
let jack: User & AgeType = { name:'jack', age: 30 }

注意:如果多个接口类型存在同名属性,比如一个是number,一个string类型,那么合并出来的是无用类型。

type obj = { id: number; name: string; } 
  & { age: number; name: number; };
  const mixed: obj = {
    id: 1,
    name: 2, // ts(2322) 错误,'number' 类型不能赋给 'never' 类型
    age: 2
  };

如果两个类型中,一个类型是另一个类型的子类型(字面量类型)那么会选择他们的子类型。

4. 联合类型

联合类型表示取值可以为多种类型中的一种,使用|来分割每一个类型

let num:string | number
num = '7'
num = 7

5. 接口

接口是对类的一部分行为进行抽象,也可以对对象的形状进行描述。

// 抽象一个Person类,参数必须与该类相同
interface Person {
    readonly name: string; // 表示该属性是只读属性赋值会报错
    age: number;
}

const jack:Person = {
    name: 'Jack',
    age:30
}

// 少字段会报错
const tom:Person = {
    name: 'Tom',
}

// 多字段也会报错
const tom:Person = {
    name: 'Tom',
    age: 30,
    money: 100
}

也可以使用允许其他的任意属性,可以使用索引标签的形式来满足要求。需要注意的是一旦定义了任意属性,确定属性和可选属性的类型都必须是它的类型的子集。

// 基本写法
interface Person {
    name: string;
    age?: number;
    // [propName: string]: string // 可选属性必须是必选属性的子类型
    [propName: string]: string | number | undefined
}

let tom: Person = {
    name: 'Tom',
    gender: 'male'
};

// 报错 Property 'age' of type 'number' is not assignable to string index type 'string'.
let jack: Person = {
  name: 'jack',
  age: 25,
  gender: 'male'
}

接口继承的写法

interface User {
    id: number;
    name: string;
}

interface Person extends User {
    age: number;
}
const Tom:Person = {
    id: 1,
    name: 'tom',
    age: 30
}

接口与类型的区别:

  1. 接口通过interface定义,type用来定义类型的别名。
  2. 接口可以重定义,type不行。接口重复定义时,后者接口是对前者接口的一种补充。
  3. 接口可以使用extends进行继承,type可以通过联合类型来模拟继承。

6. 泛型

泛型就是当定义变量无法确定其类型时,可以采用泛型的方法。

function identity<T>(value:T):T{
    return value;
}

identity<number>(1)
identity<string>('jack')

理解如下:

传递多个参数类型:

function identity<T,U>(x:T,y:U):T{
    return x;
}
identity<number,number>(1,2)
identity<string,number>('a',2)

也可以对泛型的参数类型进行约束

interface Sizeable{
  size: number
}
function identity<T extends SizeAble>(arg: T):T{
  console.log(arg.size)
  return arg
}

更多泛型用法可以参考文章:juejin.cn/post/701880…

7. 总结

7.1. 基本类型总结

  • 能确定类型的,尽量定义类型。
  • 无法确定类型的,可以使用 any 进行兜底。
  • 当函数没有返回值时,可以使用void定义。
  • any和unknown可以接收任意类型值,any可以赋值给任意类型,但unknown不可以赋值给任意类型。
  • void和any在项目中是比较常见的,never和unknown不常用。就比如,最近在调试PC端渲染组件的时候,会经常碰到。

8. 参考文章

  1. TypeScript官方文档:www.typescriptlang.org/zh/
  2. TypeScript史上最强入门文章:juejin.cn/post/701880…