js 类型分为两种:基本数据类型和复杂数据类型

基本数据类型主要有: number string boolean null undefined symbo (es6新增)、 BigInt (es10新增)

ts 作为 js 的超集,是依附 js 的,有着 js 的所有特性。ts 独有的数据类型: any void never unknown 、元组(Tuble)

接下来介绍一下 ts 的各种数据类型

基础数据类型

any 、unknown

any 表示任意类型

可以赋予任意类型的值。但这样会逃避类型检查, any 类型的变量可以做任何操作,编辑时不会报错

let name: any = 'Jane' // ok
let flag: any = true // ok
let age: any = 15 // ok

unknown 表示未知的类型

any 的区别: unknown 类型的值赋值给别的类型会报错, any 不会

let u: unknown = 'hello'
let a: any = 'hello'
let name: string
name = u // error:Type 'unknown' is not assignable to type 'string'
name = a // ok

number、string、boolean

number 表示数值类型

let num: number = 1

string 表示字符串类型

let str: string = 'Jane'

也可以使用es6 模板字符串

let name = 'Sasa'
let str: string = `my name is ${name}`

不能使用new String() 声明字符串,会返回一个Object

boolean 表示布尔值 true 和 false

let flag: boolean = true

不能使用new Boolean() 声明布尔值,因为会返回一个Object

数组、元组

  • 可以使用 类型 + [] 定义数组
  • let arr: number[] = [1, 2, 3]

    以上代码表示 :数组内部所有值必须为数值类型,否则就会报错

    let arr: number = [1, '2', 3] // error:Type '(string | number)[]' is not assignable to type 'number'.
  • 数组泛型定义数组
  • let arr: Array<number> = [1, 2, 3]
  • 接口定义数组
  • interface ItemType {
        name: string;
        age: number
    interface ListType {
        [index: number]: ItemType
    let list: ListType = [{name: 'Sasa', age: 15}]

    表示 已知元素数量 类型 的数组,长度需固定,对应位置的类型需要相同

    下面定义一个 string number 的元组

    let tupleList: [string, number]
    tupleList = ['Jane', 16]

    never、void

    表示空,一般用于表示函数没有返回值

    // 表示该方法没有返回值
    function hello(): void{
        alert('hello world')
    

    如果声明一个void类型的变量,并没有什么用,因为只能赋值undefinednull

    let unusable: void = undefined;
    let unusable: void = null;

    never

    表示那些不存在的值的类型,一般用于函数类类型声明,表示永远不会有返回值,比如函数抛出错误

    function error(message: string): never {
        throw new Error(message);
    

    null 和 undefined

    和void类型,这两个类型用处不大

    let u: undefined = undefined
    let n: null = null

    默认情况下nullundefined是所有类型的子类型,就是说你可以把nullundefined赋值给其他类型的变量

    但是为了避免很多问题,可以在配置文件中指定strictNullChecks进行限制这种做法(严格空值检查模式)

    "compilerOptions": { // ... "strictNullChecks": true

     这样在赋值的时候,会有提示:

    let str: string = undefined //error:Type 'undefined' is not assignable to type 'string'.

    接口 (Interfaces)

    接口类似对象,描述了一组数据结构,用于对函数、对象、数组等进行结构类型检查

    // 定义一个对象参数接口
    interface PersonType {
        name: string
    function func(person: PersonType) {
        console.log(person['name']) // Jane
    let person = {name: 'Jane', age: 16, cardID: '130***2025'}
    func(person)

    使用interface定义一个接口,进行类型注解。按上面举例来说,就是要求这个函数参数有一个名为name、类型为string的属性

    实际上我们传入的会有很多属性,但编译器只会检查接口中必须的属性是否存在,并且类型是否匹配

    上面例子中,必须属性就是name,所以定义的值中必须有name属性、类型必须匹配。其他属性如age、cardId,编译器则不会进行检查

    对象属性不全是必须的,有些只是在某种条件下存在 。  定义可选属性,就是在名称后面加一个?

    可选属性的好处就是对可能存在的属性进行预定义,避免引用不存在的属性出错

    interface PersonType {
        name: string,
        age?: number
    function func(person: PersonType) {
        console.log(person['name']) // Jane
        console.log(person['age']) // undefined
    let person = {name: 'Jane'}
    func(person)

    让我们再看一个例子,请看下面例子有什么问题

    interface PersonType {
        name?: string,
        age?: number
    function func(person: PersonType) {
        console.log(person.name.toLocaleLowerCase())
    func({age: 16})

    上面例子因为name属性不存在,对他做处理则会报错,所以需要加个判断,如下

    console.log(person.name?.toLocaleLowerCase())

    有一些属性只能在对象创建的时候,进行赋值。 可以使用readonly创建只读属性,只读属性不可修改

    interface PersonType {
        readonly name: string
    let person: PersonType = {name: 'Jane'}
    person.name = 'Sasa' // error: Cannot assign to 'name' because it is a read-only property.

    额外的属性检查

    一个对象会有很多属性,在我们不确定某些键值的情况下,直接向对象添加,如下:

    interface PersonType {
        name?: string
    let person: PersonType = {}
    person.cardID = '111' //  error:Property 'cardID' does not exist on type 'PersonType'.

     会有报错,PersonType不存在这个属性,这时我们可以添加一个通用字段[key: string]: any

    interface PersonType {
        name?: string,
        [key: string]: any
    let person: PersonType = {}
    person.cardID = '111' 

     其他例子:

    interface PersonType {
        name: string,
        age: number,
        [key: string]: any
    function func(person: PersonType) {
        console.log(person['name'])
        console.log(person['cardID'])
    let person = {name: 'Jane', age: 16, cardID: '130***2025'}
    func(person)

    函数需要注解的部分其实只有 参数值 和 返回值

    let checkFunc = (str: string): boolean => {
        return str['includes']('a')
    checkFunc('abc')

    以上代码表示这个函数:接收一个string参数,返回一个boolean的结果

    使用接口注解函数

    使用接口定义函数类型,描述的是一个参数列表 和 返回值类型, 参数列表里每个参数都需要名字和类型

    interface CheckFuncConfig {
        // 参数列表    // 返回值
        (str: string): boolean
    let checkFunc: CheckFuncConfig = (str) => {
        return str['includes']('a')
    checkFunc('abc')

    就像接口中可选属性一样,函数也存在可选参数

    let people: (name: string, age?: number) {
        // ...
    

    参数名和接口定义的可以不一样

    对于函数类型的类型检查来说,函数的参数名不需要与接口里定义的名字相匹配,但是会逐个检查,要求对应位置上的参数类型是一样的

    就像下面代码块,上面接口定义的字段是str,但也可以接受成其他字段,比如character

    interface CheckFuncConfig {
         // 参数列表    // 返回值
        (str: string): boolean
    let checkFunc: CheckFuncConfig = (sss) => {
        return character['includes']('a')
    checkFunc('abc')

    在 js 里,函数时常会有这种场景:根据传入不同的参数而返回不同类型的数据。

    看一下如下例子:传入的如果是字符串,返回参数在名称列表的索引;传入的如果是数字,返回名称列表这个索引位置的数据

    let nameList = ['Jane', 'Sasa', 'Aba']
    function getName(name): any{
        if(typeof name === 'number'){
            return nameList[name]
        } else if(typeof name === 'string'){
            return nameList.indexOf(name)
    console.log(getName('Sasa')) // 1
    console.log(getName(1)) // Sasa

    接下来讨论怎么能在类型系统中,表现这种情况:

    类型推断更适用于 参数只有一种类型的情况

    在这个例子中,参数有多种参数类型。这时需要用到 函数重载。下面使用这种方法重写上面的例子:

    let nameList = ['Jane', 'Sasa', 'Aba']
    function getName(name: string): number
    function getName(name: number): string
    function getName(name): any{
        if(typeof name === 'number'){
            return nameList[name]
        } else if(typeof name === 'string'){
            return nameList.indexOf(name)
    console.log(getName('Sasa')) // 1
    console.log(getName(1)) // Sasa

    上面多了两行代码。为getName函数提供两个函数类型定义,每个函数类型定义就是一个重载,这样组成了一个重载列表,编译器会在其中寻找匹配的类型注解。

    高级数据类型

    联合是或的关系,类似if判断里面 ||,满足其中一个即可

    1、基础类型联合

    let a: string | number
    a = 1   // ok
    a = '1' // ok

     2、对象类型联合

    interface Foo {
      foo: string;
      name: string;
    interface Bar {
      bar: string;
      name: string;
    const sayHello = (obj: Foo | Bar) => { /* ... */ }
    sayHello({ foo: "foo", name: "lolo" })  // ok
    sayHello({ bar: "bar", name: "growth" }) // ok

    交叉是并集的关系,两者合并到一起,类似if判断 && 的关系,两者都要达到要求

    interface personType1 {
        name: string,
        age: number
    interface personType2 {
        height: number
    const person = (info: personType1 & personType2) => {
        console.log(info.name)   // Jane
        console.log(info.age)    // 16
        console.log(info.height) // 175
    person({name: 'Jane', age: 16, height: 175})

     初始化一个变量的时候,没有定义类型,会按照类型推论规则,推断出一个类型

    let name = 'Sasa'
    name = 1 // error: Type '1' is not assignable to type 'string'.

    注:如果定义的时候没有赋值,不管之后有没有赋值,都会被推断成any类型,继而后面不会被类型检查

    let name
    name = 1 // ok

    泛型 (Generics)

    泛型是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。

    在不使用泛型的情况下,进行函数注解是这样:

    function example(arg: number): number {
        return arg
    

    如果参数是多个类型:

    function example(arg: number | string): number | string{
        return arg
    

    这样写会显的繁琐,臃肿

    接下来,看一下使用泛型定义函数

    function example<T>(arg: T): T {
        return arg
    

    泛型使用 < > 声明一个类型变量TT帮助捕获用户传入的数据类型,这样就可以使用这个类型进行注解

    多个类型参数

    定义多个类型参数:

    function swap<T, U>(tuple: [T, U]): [U, T]{
        return [tuble[1], tuble[0]]
    swap([7, 'seven']) // ['seven', 7]

    上例中,定义了一个swap函数,用来交换数据位置的元组

    在函数内部使用泛型变量的时候,由于事先不知道它是哪种类型,所以不能随意的操作他的属性或方法

    我们定义一个 打印 参数长度的函数:

    function printLength<T>(arg: T): T {
        console.log(arg.length) // error:类型“T”上不存在属性“length”
        return arg
    

    编译器不能确定每种类型都有length属性,所以就会报错 类型“T”上不存在属性“length”

    为此,我们需要进行约束参数,不能传入不含有length属性的参数进来

    约束实现:创建一个包含length属性的接口,使用extends和接口关联,来实现约束:

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

     对这个泛型函数加了约束之后,就不能随意传入各种类型的值,必须包含必须的属性:

    printLength('hello world') // ok
    printLength([1, 2, 3]) // ok
    printLength({ length: 2 }) // ok
    printLength(1) // error:类型“number”的参数不能赋给类型“LengthType”的参数

    用来对联合类型进行遍历

    type People = 'name' | 'desc' | 'address'
    type PeopleInfo = {
      [p in People]: string
    // 等同于
    type PeopleInfo = {
        name: string,
        desc: string,
        address: string
    

    keyof 

    获取类型的所有键,返回一个联合类型

    interface People{
        name: string,
        desc: string,
        address: string
    type keys = keyof People // 'name' | 'desc' | 'address'

    工具类 (Utility Types)

    Partial<Type> 可选类型

    使用Partial,使类型中的所有属性都变为 可选项

    interface Todo {
      title: string,
      desc: string
    // 等同于
    // interface Todo {
    //   title?: string,
    //   desc?: string
    function updateTodo(fields: Partial<Todo>) {
      return { ...fields }
    updateTodo({ title: '标题' })

    Required<Type> 必填类型

    partial相反,使用Required,使类型中的每一项都变为 必填项

    interface Todo {
      title: string,
      desc: string
    function updateTodo(fields: Required<Todo>) {
      return { ...fields }
    updateTodo({ title: '标题' })
    //error:类型 "{ title: string; }" 中缺少属性 "desc",但类型 "Required<Todo>" 中需要该属性。

    Exclude<UnionType, ExcludedMembers>

    Exclude<T, U>不包含:返回类型T中不包含U的部分,类似求差集

    type Names = Exclude<'Jane' | 'Sasa' | 'Annie', 'Annie'>
    // 等同于
    // type Names = 'Jane' | 'Sasa'

     定义变量的时候,也只能是这部分中的一个,如果是集合外的,会报错

    let name: Names = 'Jane' // ok
    let name: Names = 'Ella' // error:不能将类型“"Ella"”分配给类型“"Jane" | "Sasa"”

    Extract<Type, Union>

    返回类型TU的所有交集

    type Names = Extract<'Jane' | 'Sasa' | 'Annie', 'Annie'>
    // 等同于
    // type Names = 'Annie'
    let name: Names = 'Annie' // ok
    let name: Names = 'Ella' // error:不能将类型“"Ella"”分配给类型“"Annie"”

    Readonly<Type> 只读类型

    将所有属性变为只读,不可更改

    interface Todo {
      title: string,
      desc: string
    const todo: Readonly<Todo> = {
      title: 'hello',
      desc: 'hello world'
    todo.title = 'say hello' // error:无法为“title”赋值,因为它是只读属性

    Pick<Type, Keys>

    用于从对象中抽取属性,组成一个新的类型

    interface Todo {
      title: string,
      desc: string,
      category: string
    type TodoType = Pick<Todo, 'title' | 'category'>
    const todo: TodoType = {
      title: 'blog',
      category: 'vue'
    

    从接口中抽取title、category两个属性组成一个新的类型

    Omit<Type, Keys>

    用于从对象中抽取属性然后删除,将对象剩余的属性组成一个新的类型

    interface Todo {
      title: string,
      desc: string,
      category: string
    type TodoType = Omit<Todo, 'desc'>
    const todo: TodoType = {
      title: 'blog',
      category: 'vue'
    

    删除接口中的desc属性,剩余的属性组成一个新的类型

    NonNullable

    从类型中剔除 null undefined

    type T0 = NonNullable<string | number | undefined>;
    // 等同于
    // type T0 = string | number
    type T1 = NonNullable<string[] | null | undefined>;
    // 等同于
    // type T1 = string[]

    ReturnType 返回值类型

    获取函数返回值的类型

    type IntervalHandle = ReturnType<typeof setInterval>
    const timer: IntervalHandle = setInterval(() => { })
    type T0 = ReturnType(() => string)
    type T1 = ReturnType<() => 1>

    Record<Keys, Type>

    构造一个对象类型,其键为Keys,值为Type

    interface PeopleInfo {
      age: number,
      address: string
    type PeopleName = 'Jane' | 'Ella' | 'Sasa'
    const peoples: Record<PeopleName, PeopleInfo> = {
      Jane: { age: 10, address: '北京市' },
      Ella: { age: 11, address: '北京市' },
      Sasa: { age: 12, address: '北京市' }
    

    Parameters

    获取函数的参数类型,然后将每个参数类型放进一个元组中

    tpye T0 = Parameters<() => string> 
    // type T0 = []
    type T1 = Parameters<(s: string) => void> 
    // type T1 = [s: string]
    type T1 = Parameters<(s: string, n: number) => void>
    // type T1 = [s: string, n: number]

    本文来自博客园,作者:时光凉忆,转载请注明原文链接:https://www.cnblogs.com/naturl/p/17046689.html