TypeScript 基础

88 阅读9分钟

作用

  • 代码检测

  • 类型推断

基本类型

Boolean 类型

Number 类型

String 类型

Enum 类型

  • 初始值默认为 0 其余的成员会会按顺序自动增长 可以理解为数组下标
enum Color {
RED= 2,
PINK,
BLUE,
}
const red: Color = Color.RED;
console.log(pink); // 3
  • 常量枚举

    • 整个枚举会在编译阶段被删除
    const enum Color {
    RED,
    PINK,
    BLUE,
    }
    const color: Color[] = [Color.RED, Color.PINK, Color.BLUE];
    console.log(color); //[0, 1, 2]
    
    // 编译之后的js如下:
    var color = [0 /* RED */, 1 /* PINK */, 2 /* BLUE */];
    // 可以看到我们的枚举并没有被编译成js代码 只是把color这个数组变量编译出来了
    

Array 类型

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

元组(tuple)类型

  • 只能表示一个已知元素数量和类型的数组

    • 长度已指定,越界访问会提示错误

    • 一个数组中数量和类型都不确定:any[]

const tuple: [number, string] = [1, "xianzao"];

undefined和null

  • 可以把 null 和 undefined 赋值给其他类型

  • undefined 可以给 void 赋值,null不行

let a: undefined = undefined;
let b: null = null;

let str: string = 'xianzao';
str = null; // 编译正确
str = undefined; // 编译正确

let c:void = undefined // 编译正确
let d:void = null // 编译错误

any 类型

  • any会跳过类型检查器对值的检查,任何值都可以赋值给any类型

void 类型

  • 函数没有返回值

never 类型

  • 总是会抛出异常或根本就不会有返回值的函数表达式或箭头函数表达式的返回值类型

    • 函数执行时抛出了异常

    • 函数中执行无限循环的代码

// 异常
function error(msg: string): never { // 编译正确
throw new Error(msg);
}
 
// 死循环
function loopForever(): never { // 编译正确
while (true) {};
}

Unknown 类型

  • 所有类型都可以分配给unknown

  • unknown 任何类型的值都可以赋值给它,但它只能赋值给unknown和any

  • 任何类型的值可以赋值给any,同时any类型的值也可以赋值给任何类型

对象类型

object, Object 和 {} 类型

object

  • 所有的非原始类型

    • 不能把 number、string、boolean、symbol等 原始类型赋值给 object

    • 在严格模式下,null 和 undefined 类型也不能赋给 object

let object: object;
object = 1; // 报错
object = "a"; // 报错
object = true; // 报错
object = null; // 报错
object = undefined; // 报错
object = {}; // 编译正确

Object

  • 拥有 toString、hasOwnProperty 方法的类型

  • 所有原始类型、非原始类型都可以赋给 Object(严格模式下 null 和 undefined 不可以)

{}

  • 原始类型和非原始类型的集合

  • Class 关键字
class Person {
    name: string
    age: number
    constructor(name: string, age: number) {
        this.name = name
        this.age = age
    }
    sayHi(): void {
    console.log(`Hi, ${this.name}`)
    }
}

数组

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

函数

// 函数表达式 
const add = function(x: number, y: number): number {
return x + y;
}

// 接口定义函数 
interface Add {
(x: number, y: number): number;
}

// 剩余参数  
function add(...numbers: number[]): number {
let sum = 0;
for (let i = 0; i < numbers.length; i++) {
sum += numbers[i];
}
return sum;
}

// 函数重载:使用相同名称和不同参数数量或类型创建多个方法的一种能力
function add(x: any, y: any): any {

return x + y;

}

类型推论

let x = 1;
x = true; // 报错
  • 如果没有明确的指定类型,那么 TypeScript 会依照类型推论的规则推断出一个类型

  • 定义的时候没有赋值,不管之后有没有赋值,都会被推断成 any 类型而完全不被类型检查

类型断言

  • 尖括号写法

  • as 写法

let str: any = "xianzao";
let strLength: number = (<string>str).length;

let str: any = "xianzaoe";
let strLength: number = (str as string).length;

非空断言

  • x!的值不会为 null 或 undefined

确定赋值断言

  • 定义了变量, 没有赋值就使用,则会报错

联合类型

  • 联合类型用|分隔,表示取值可以为多种类型中的一种

类型别名

  • 没有创建新类型

  • 类型别名常用于联合类型

type count = number | number[];
function hello(value: count) {}

交叉类型

  • 用&操作符表示,多个类型的并集

    • 如果key相同但是类型不同,则该key为never类型
interface IpersonA {
name: string
}
  
interface IpersonB {
name: number
}

function testAndFn(params: IpersonA & IpersonB) {
console.log(params)
}

testAndFn({name: "xianzao"}) // error TS2322: Type 'string' is not assignable to type 'never'.

类型守卫

  • 类型守卫是运行时检查,确保一个值在所要类型的范围内

    1、in关键字

    2、typeof 关键字

    3、instanceof

    4、自定义类型保护的类型谓词

// 1、in关键字  
interface InObj1 {
    a: number,
    x: string
}

interface InObj2 {
    a: number,
    y: string
}

function isIn(arg: InObj1 | InObj2) {
    // x 在 arg 打印 x
    if ('x' in arg) console.log('x')
    // y 在 arg 打印 y
    if ('y' in arg) console.log('y')
}

isIn({a:1, x:'xxx'})
isIn({a:1, y:'yyy'})

// 2、typeof 关键字  
function isTypeof( val: string | number) {
    if (typeof val === "number") return 'number'
    if (typeof val === "string") return 'string'
    return 'nothing'
}

// 3、instanceof  
function creatDate(date: Date | string){
    console.log(date)
    if(date instanceof Date){
        date.getDate()
    }else {
        return new Date(date)
    }
}

// 4、自定义类型保护的类型谓词 
function isNumber(num: any): num is number {
    return typeof num === 'number';
}

function isString(str: any): str is string{
    return typeof str=== 'string';
}

接口

  • 关键字interface来声明接口

    • 接口一般首字母大写

    • 习惯 I 大写字母开头

interface Person {
    name: string;
    age: number;
}

let xianzao: Person = {
    name: 'xianzao',
    age: 18
    };

设置接口可选|只读

  • ?

  • ReadonlyArray 类型

索引签名

  • 接口还允许有其他的任意属性

  • eg. string 类型的值来索引,索引到的是一个 any 类型的值

interface Person {
    name: string;
    age?: number;
    [prop: string]: any;
}
  
const p1:Person = { name: "xianzao" };
const p2:Person = { name: "zaoxian", age: 28 };
const p3:Person = { name: "test", sex: 1 }

接口与类型别名的相同点

  • 都可以用来描述对象或函数的类型

    type MyTYpe = {
        name: string;
        say(): void;
    }
    
    interface MyInterface {
        name: string;
        say(): void;
    }
    
  • 都允许扩展

    • interface 用 extends 来实现

    • type 使用 & 实现扩展

    interface MyInterface {
      name: string
      say(): void
    }
    
    interface MyInterface2 extends MyInterface {
      sex: string
    }
    
    let person: MyInterface2 = {
      name: 'xianzao',
      sex: 'male',
      say(): void {
        console.log('hello xianzao')
      },
    }
    
    type MyType = {
      name: string
      say(): void
    }
    type MyType2 = MyType & {
      sex: string
    }
    let value: MyType2 = {
      name: 'xianzao',
      sex: 'male',
      say(): void {
        console.log('hello xianzao')
      },
    }
    

接口与类型别名的不同点

  • type可以声明基本数据类型别名/联合类型/元组等,而interface不行

  • interface能够合并声明,而type不行

// 基本类型别名
type UserName = string
type UserName = string | number
// 联合类型
type Animal = Pig | Dog | Cat
type List = [string, boolean, number]

interface Person {
  name: string
}
interface Person {
  age: number
}
// 此时Person同时具有name和age属性

泛型

  • 不预先指定具体的类型,而在使用的时候再指定类型的一种特性

基本使用

function getValue<T, U>(arg: [T = string, U]): [T, U] {
  return arg
}

// 使用
const str = getValue(['xianzao', 18])

泛型约束

  • 用extends关键字来对泛型进行约束

    • str,arr 还是obj,只要具有 length 属性,都可以
interface Lengthwise {
  length: number
}

function getLength<T extends Lengthwise>(arg: T): T {
  console.log(arg.length)
  return arg
}

const str = getLength('xianzao')
const arr = getLength([1, 2, 3])
const obj = getLength({ length: 5 })

泛型接口

interface KeyValue<T, U> {
  key: T
  value: U
}

const person1: KeyValue<string, number> = {
  key: 'xianzao',
  value: 18,
}
const person2: KeyValue<number, string> = {
  key: 20,
  value: 'zaoxian',
}

泛型类

class Test<T> {
  value: T
  add: (x: T, y: T) => T
}

let myTest = new Test<number>()
myTest.value = 0
myTest.add = function (x, y) {
  return x + y
}

泛型类型别名

type Cart<T> = { list: T[] } | T[]
let c1: Cart<string> = { list: ['1'] }
let c2: Cart<number> = [1]

泛型工具类型

  • typeof

    • 类型保护,推出类型
    //先定义变量,再定义类型
    let p1 = {
      name: 'xianzao',
      age: 18,
      gender: 'male',
    }
    type People = typeof p1
    function getName(p: People): string {
      return p.name
    }
    getName(p1)
    
  • keyof:获取一个对象接口中的所有 key 值

    interface Person {
      name: string
      age: number
      gender: 'male' | 'female'
    }
    
    type PersonKey = keyof Person //type PersonKey = 'name'|'age'|'gender';
    
    function getValueByKey(p: Person, key: PersonKey) {
      return p[key]
    }
    let val = getValueByKey({ name: 'xianzao', age: 18, gender: 'male' }, 'name')
    console.log(val) // xianzao
    
  • in:遍历枚举类型

    type Keys = 'a' | 'b' | 'c'
    
    type Obj = {
      [p in Keys]: any
    } // -> { a: any, b: any, c: any }
    
  • infer:用它取到函数返回值的类型方便之后使用

type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any
  • 索引访问操作符: []
interface Person {
  name: string
  age: number
}

type x = Person['name'] // x is string

内置工具类型

  • Required:将类型的属性变成必选

    interface Person {
      name?: string
      age?: number
      hobby?: string[]
    }
    
    const user: Required<Person> = {
      name: 'xianzao',
      age: 18,
      hobby: ['code'],
    }
    
  • Partial:将所有属性转换为可选属性

    interface Person {
      name: string
      age: number
    }
    
    const xianzao: Person = {
      name: 'xianzao',
    } // error  Property 'age' is missing in type '{ name: string; }' but required in type 'Person'.
    
    type User = Partial<Person>
    
    const xianzao: User = {
      name: 'xianzao',
    } // 编译正确
    
  • Exclude:将某个类型中属于另一个的类型移除掉,剩余的属性构成新的类型

    type T0 = Exclude<'a' | 'b' | 'c', 'a'> // "b" | "c"
    type T1 = Exclude<'a' | 'b' | 'c', 'a' | 'b'> // "c"
    type T2 = Exclude<string | number | (() => void), Function> // string | number
    
  • Extract<T,U> 从 T 中提取出 U

    type T0 = Extract<'a' | 'b' | 'c', 'a' | 'f'> // "a"
    type T1 = Extract<string | number | (() => void), Function> // () =>void
    
  • Readonly:把数组或对象的所有属性值转换为只读的,这些属性不能被重新赋值

    interface Person {
      name: string
      age: number
      gender?: 'male' | 'female'
    }
    
    let p: Readonly<Person> = {
      name: 'hello',
      age: 10,
      gender: 'male',
    }
    p.age = 11 // error  Cannot assign to 'age' because it is a read-only property.
    
  • Record<K extends keyof any, T> 的作用是将 K 中所有的属性的值转化为 T 类型

    type Property = 'key1' | 'key2'
    type Person = Record<Property, string>
    
    const p: Person = {
      key1: 'hello',
      key2: 'xianzao',
    }
    
  • Pick:从某个类型中挑出一些属性出来

    type Person = {
      name: string
      age: number
      gender: string
    }
    
    type P1 = Pick<Person, 'name' | 'age'> // { name: string; age: number; }
    
    const user: P1 = {
      name: 'xianzao',
      age: 18,
    }
    
  • Omit<T,K> 从T中取出除去K的其他所有属性

    interface Person {
      name: string
      age: number
      gender: string
    }
    type P1 = Omit<Person, 'age' | 'gender'>
    const user: P1 = {
      name: 'xianzao',
    }
    
  • NonNullable:去除类型中的 null 和 undefined

    type P1 = NonNullable<string | number | undefined> // string | number
    type P2 = NonNullable<string[] | null | undefined> // string[]
    
  • ReturnType:得到一个函数的返回值类型

    type Func = (value: string) => string
    const test: ReturnType<Func> = '1'
    
  • Parameters:获得函数的参数类型所组成的元组类型

    type P1 = Parameters<(a: number, b: string) => void> // [number, string]
    
  • InstanceType:返回构造函数类型T的实例类型

    class C {
      x = 0
      y = 0
    }
    
    type D = InstanceType<typeof C> // C
    

tsconfig.json

{
  "compilerOptions": {

    /* 基本选项 */
    "target": "es5",                       // 指定 ECMAScript 目标版本: 'ES3' (default), 'ES5', 'ES6'/'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'
    "module": "commonjs",                  // 指定使用模块: 'commonjs', 'amd', 'system', 'umd' or 'es2015'
    "lib": [],                             // 指定要包含在编译中的库文件
    "allowJs": true,                       // 允许编译 javascript 文件
    "checkJs": true,                       // 报告 javascript 文件中的错误
    "jsx": "preserve",                     // 指定 jsx 代码的生成: 'preserve', 'react-native', or 'react'
    "declaration": true,                   // 生成相应的 '.d.ts' 文件
    "sourceMap": true,                     // 生成相应的 '.map' 文件
    "outFile": "./",                       // 将输出文件合并为一个文件
    "outDir": "./",                        // 指定输出目录
    "rootDir": "./",                       // 用来控制输出目录结构 --outDir.
    "removeComments": true,                // 删除编译后的所有的注释
    "noEmit": true,                        // 不生成输出文件
    "importHelpers": true,                 // 从 tslib 导入辅助工具函数
    "isolatedModules": true,               // 将每个文件做为单独的模块 (与 'ts.transpileModule' 类似).

    /* 严格的类型检查选项 */
    "strict": true,                        // 启用所有严格类型检查选项
    "noImplicitAny": true,                 // 在表达式和声明上有隐含的 any类型时报错
    "strictNullChecks": true,              // 启用严格的 null 检查
    "noImplicitThis": true,                // 当 this 表达式值为 any 类型的时候,生成一个错误
    "alwaysStrict": true,                  // 以严格模式检查每个模块,并在每个文件里加入 'use strict'

    /* 额外的检查 */
    "noUnusedLocals": true,                // 有未使用的变量时,抛出错误
    "noUnusedParameters": true,            // 有未使用的参数时,抛出错误
    "noImplicitReturns": true,             // 并不是所有函数里的代码都有返回值时,抛出错误
    "noFallthroughCasesInSwitch": true,    // 报告 switch 语句的 fallthrough 错误。(即,不允许 switch 的 case 语句贯穿)

    /* 模块解析选项 */
    "moduleResolution": "node",            // 选择模块解析策略: 'node' (Node.js) or 'classic' (TypeScript pre-1.6)
    "baseUrl": "./",                       // 用于解析非相对模块名称的基目录
    "paths": {},                           // 模块名到基于 baseUrl 的路径映射的列表
    "rootDirs": [],                        // 根文件夹列表,其组合内容表示项目运行时的结构内容
    "typeRoots": [],                       // 包含类型声明的文件列表
    "types": [],                           // 需要包含的类型声明文件名列表
    "allowSyntheticDefaultImports": true,  // 允许从没有设置默认导出的模块中默认导入。

    /* Source Map Options */
    "sourceRoot": "./",                    // 指定调试器应该找到 TypeScript 文件而不是源文件的位置
    "mapRoot": "./",                       // 指定调试器应该找到映射文件而不是生成文件的位置
    "inlineSourceMap": true,               // 生成单个 soucemaps 文件,而不是将 sourcemaps 生成不同的文件
    "inlineSources": true,                 // 将代码与 sourcemaps 生成到一个文件中,要求同时设置了 --inlineSourceMap 或 --sourceMap 属性

    /* 其他选项 */
    "experimentalDecorators": true,        // 启用装饰器
    "emitDecoratorMetadata": true          // 为装饰器提供元数据的支持
  }