TypeScrip入门基础

文中示例可直接在 playground 上运行。

一、类型

1️⃣. 基础类型

boolean string number enum undefined null Array tuple any unknown void never

1、boolean、string、number
const isRich: boolean = true
const name: string = "Tony"
const age: number = 18

console.log(typeof isRich)
console.log(typeof name)
console.log(typeof age)
2、enum
enum Animal {
    CAT,
    DOG
}
const animal: Animal = Animal.DOG
console.log(animal) // 打印 1

const 修饰修饰枚举在ts转换成js时,不会保留枚举的特性,可在playground验证。

 const enum City {
    SH = "shanghai",
    BJ = "chengdu", 
    SZ = "shenzhen"
}
const shanghai: City = City.SH
console.log(shanghai)
3、null、undefined

默认情况下,可以把 undefined 和 null 赋值给其他类型,
但是再配置文件tsconfig.json中 配置 “strictNullChecks”:true后则不可以了

// 开启了 "strictNullChecks":true 
let u: undefined = undefined
let n: null = null

// const name: string = u // 报错
// u = n // 不能相互赋值

// undefined 可以赋值给 void
// null 不能复制给void
const a: void = u 
// const b: void = n // 会报错
4、Array、tuple

元组(tuple) 规定了已知长度和类型的数组。

// 普通的数组
const arr: string[] = ['cat', 'dog']
//等价于
const arr1: Array<string> = ['cat', 'dog']

// tuple元组是严格的数组
const xuanyu: [string, number, string] = ['炫羽', 18, '男']
5、never、void

void常用在函数上,表明函数没有返回值。

function fn(): void {
    console.log('炫一个?')
}
fn()

never表示永远也不会执行完的函数。

function fn(): never {
    while(true){}
}

// 抛出异常
function errFn(): never {
    throw new Error()
}
6、unknown、any

any: 可以跳过类型检查,任何类型都可以复制给any。

// any -> 任何类型赋值给any
const MyName: any = '炫羽'
const myMoney: any = 100000000
const isAnimal: any = true
// any -> any赋值给其他任何类型
const newName: string = MyName
const dog: boolean = isAnimal

unknown: 和any一样。区别unknown只能赋值给unknown和any。

// any -> 任何类型赋值给any
const MyName: unknown = '炫羽'
const myMoney: unknown = 100000000
const isAnimal: unknown = true

// any -> any赋值给其他类型
// const newName: string = MyName // 报错
const money: unknown = 100000000 // 正确
const dog: unknown = isAnimal // 正确

2️⃣.对象(Object)类型

函数 对象 数组 类

1、函数

函数是用的非常多的类型了。

  • 基本 - 用法

    const fn: () => void = () => {
        console.log('666')
    }
    
  • 默认参数 - 用法

    function fn(age: number = 18):void {
        console.log(age)
    }
    fn()
    
  • 可选参数 - 用法

    function sum(a: number, b: number = 1, c?: number) {
        if(c) {
                return a + b + c
            }
        return a + b
    }
    console.log(sum(1,2)) // 3
    console.log(sum(1,2,3))// 6
    
  • 剩余参数(必须是一个数组类型)

    function sum(...numbers: number[]) {
        let res = 0
        for(let i = 0; i < numbers.length; i++) {
            res += numbers[i]
        }
        return res
    }
    console.log(sum(1,2)) // 3
    console.log(sum(1,2,3))// 6
    
2、对象、数组、类
  • 对象
    一般用 接口:interface 来实现类似的功能,所以此处不在深入。
    let a: object = {}
    let b: object = { name: '炫羽' }
    let c: {}
    c  = { name: '炫羽' }
    console.log(c)
    
  • 数组
    // 普通的数组
    const arr: string[] = ['dog', 'cat']
    //等价于
    const arr1: Array<string> = ['dog', 'cat']
    
  • class People {
        name: string
        age: number
        constructor(name:string, age: number) {
            this.name = name
            this.age = age
        }
        info() {
            console.log('名字:' + this.name)
            console.log('年龄:' + this.age)
        }
    }
    

3️⃣ 接口 ( interface )

一般首字母大写。接口类型的需要声明全部的属性或方法。

  • 简单的例子

    interface Person {
        name: string
        age: number
        speak: () => void
    }
    const lisi: Person = {
        name: '李四',
        age: 18,
        speak() {
            console.log('666')
        }
    }
    
  • 可选属性、只读属性
    可选属性用 *?*表示。
    只读属性用 readonly。

    interface Person {
        name?: string
        readonly age: number
    }
    
    const lisi: Person = {
        // name: '李四', // 可选 可以定义也可以不定义
        age: 18
    }
    
    // readonly 修饰 只读
    // lisi.age = 19 // 报错
    
  • 鸭式辨型法
    只要传入的对象包含了接口的部分属性或者方法(鸭子的特征)那么可以认定传入的参数实现了这个接口。

    interface nameValue {
      name: string;
    }
    function getNmae(nameObj: nameValue) {
      console.log(nameObj.name);
    }
    let lisi = { age: 18, name: "我是李四" };
    getNmae(lisi); // 通过
    
    // getNmae({ age: 18, name: "我是李四" }); // 报错
    

4️⃣ 类型操作

1、联合类型 ( | )

仅需满足一个

let a: string | number = 1

a = 2 // 正确
a = 'lisi' // 正确
2、交叉类型 ( & )

需要同时满足

interface a {
    name: number
    age: number
}
interface b {
    name: string
    city: string
}
let c: (a & b) = {
    name: '炫羽',
    age: 18,
    city: '成都'
}
3、类型推断

ts会尝试推断其类型。推断不出时则为any类型。

const myName = "lisi" // 推断出 myName 是 string类型
4、类型断言 ( as )

手动指定类型,as 来指定类型.

const Myname = "abcdefg"
const count1 = (Myname as string).length
5、类型别名 ( type )

相当于给类型重新起一个名字。

interface a {
    name: number
    say: () => void
}
type str = string
type num = number
type b = a // b 是 a类型的别名
6、类型守卫 ( in 、typeof、instanceof)

当传进来的值的类型不止一种的时候,需要对值进一步的判断,确保值类型在可控的范围内。

  • in 关键字
    可以判断 某个 key 在不在其中。

    interface a {
        name: number
        age: number
    }
    interface b {
        name: string
        city: string
    }
    function fn(param: a | b) {
        if('age' in param)
         console.log(param.age)
    
        if('city' in param)
         console.log(param.city)
    }
    
  • typeof 关键字
    和js中的typeof类似,只能用于基本类型

    let x: string= "字符串不可以"
    console.log(typeof x === 'string')
    
    let y: number= 3
    console.log(typeof y === 'number')
    
  • instanceof 关键字
    instanceof 和 typeof 的区别是:instanceof 可以用于对象,且子类判断基类也是 true

    class A{
       a:number
       constructor(a1:number){
        this.a = a1
       }
    }
    class B extends A{
      b:string
      constructor(a1:number, b1:string){
        super(a1)
        this.b = b1
      }
    }
    let bbb = new B(30, "aaa")
    console.log(bbb instanceof A)
    

四、类 class

1、成员变量/方法的权限

  • public:默认值,外部和子类都能访问。
  • private:私有的,只能本类中访问,外部和子类不能访问。
  • protected:外部不能访问,子类可以访问。
  • readyonly:表示只读。

2、static 修饰符

static 修饰的变量/属性是挂载在类本身,实例化后不能通过this访问到static修饰的成员。

单例模式

class Demo {
    private static instance: Demo
    public name: string
    // 构造函数私有化,就相当于不能用 new 实例化
    private constructor(name: string) {
        this.name = name
    }
    //只能通过这一句来实例化(限制单例)
    public static getInstance() {
        // 限制单例
        if(!this.instance) {
            this.instance = new Demo("hello")
        }
        return this.instance
    }
}
const demo1 = Demo.getInstance()
const demo2 = Demo.getInstance()

console.log(demo1 === demo2) // true

3、抽象类 abstract

类似于interface的功能,abstract 修饰之后变成抽象方法,抽象类只能被继承。

abstract class Gemo {
    constructor(public width: number, height: number ) { }
    abstract getArea(): number

    public setWidth(width:number) {
        this.width = width
    }
}

class Circle extends Gemo {
    public getArea(): number {
        return this.width
    }
}
// interface 可以继承 类似于抽象类相同的功能 将相同的提取出来

五、泛型

1️⃣ 泛型的潜规则

常见泛型变量代表的意思:

  • K(Key):表示对象中的键类型;
  • V(Value):表示对象中的值类型;
  • E(Element):表示元素类型。

2️⃣ 基本使用

  • 在类中使用泛型
    interface Item {
        name: string
    }
    class DataManager<T> {
        constructor(private data: T[]) {}
        public getData(index: number) {
            return this.data[index]
        }
    }
    const dataArr = new DataManager([{name: "lisi"}])
    console.log(dataArr.getData(0))
    
  • 在接口中使用泛型
    interface KeyValue<K, V> {
      key: K;
      value: V;
    }
    

3️⃣ 泛型工具类型

泛型表示任意类型,当在某处调用了该值下的一个属性,但是该类型是不一定有该属性,所以会发生不可预期的错误,如:

function fn<T>(param: T): void {
    // param 不知道是什么具体的解构
    // console.log(param.length)

    // 所以需要增加一些工具来判断参数是什么结构
}

为了更好的约束泛型,使用一些工具来增强泛型的可读性。

1、typeof

可以获取变量、属性、函数的类型。

interface Person {
    name: string
    age: number
    getName: () => string
}

const xuanyu: Person = {name: '李四', age: 18, getName: () => '炫羽'}
type Teacher = typeof xuanyu // 类型 Teacher = 类型 Person
type Fn = typeof xuanyu.getName // 此处 fn是函数类型:() => string
const getNickname: Fn = () => {
    return '死亡一刀斩'
}
2、in

用来遍历枚举类型。

enum Words {
    A,
    B,
    C
}
type AliasWords = {
    [w in Words]: string
}

// AliasWords 的类型等价为
//  type AliasWords = {
//     0: string;
//     1: string;
//     2: string;
// }
3、extends

同时也是 es6 里引入的一个新的关键字,而且不同场景下代表的含义不一样,特此总结一下:

  • 表示继承/拓展的含义
  • 表示约束的含义
  • 表示分配的含义
3.1 继承父类的方法和属性
class Animal {
  kind = 'animal'
  constructor(kind){
    this.kind = kind;
  }
  sayHello(){
    console.log(`Hello, I am a ${this.kind}!`);
  }
}
 
class Dog extends Animal {
  constructor(kind){
    super(kind)
  }
  bark(){
    console.log('wang wang')
  }
}
 
const dog = new Dog('dog');
dog.name; //  => 'dog'
dog.sayHello(); // => Hello, I am a dog!
3.2 继承某个类型
 interface Animal {
   kind: string;
 }
 
 interface Dog extends Animal {
   bark(): void;
 }
 // Dog => { name: string; bark(): void }
3.3 泛型约束
interface Type1<T extends string> {
  name:T,
  age:number
}

let aaa:Type1<string> = {
  name:"泛型约束",
  age:18
}
3.4 条件类型与高阶类型
  type Human = {
    name: string;
  }
  type Duck = {
    name: string;
  }
  type Bool = Duck extends Human ? 'yes' : 'no'; // Bool => 'yes'
4、keyof

获取类型的所有键,返回的是键的联合类型。

interface Person {
    name: string
    age: number
    getName: () => string
}

type key1 = keyof Person // 'name' | 'age' | 'getName'
type key2 = keyof Person[] // 数组的键 比如:'length' | 'toString' | 'pop' | 'push' | 'concat' | 'join' 
type key3 = keyof string // string类型的键 比如:number | 'toString' | 'charAt' | 'charCodeAt' | 'concat'...

let aaa:Person[key1] = "你的名字"
let bbb:Person[key1] = 18
  • 当键是索引类型的时候
    键是string类型keyof返回的类型是 string | number
    键是number类型keyof返回的类型是 number
// 当是索引类型的时候
type key4 = keyof { [k: string]: boolean } // string | number
type key5 = keyof { [k: number]: boolean } // number
  • 如何使用keyof?
  • 假设我们有以下函数:
function prop(obj, key) {
  return obj[key];
}

函数中obj和key是不确定的,所以是不能确定是否可以执行 obj[key] 这个操作。
进一步优化:

function prop(obj: object, key: string) {
  return (obj as any)[key];
}

很显然加上 any 虽然可以解决燃眉之急,但这任然不是一个好的方案,还是不能确定是否可以执行 obj[key] 这个操作。用keyof可以解决这个问题:

function prop<T extends object, K extends keyof T>(obj: T, key: K) {
  return obj[key];
}

不管obj中有什么属性,key总能找到对应键值。

interface Person {
    name: string
    age: number
}

const xuanyu: Person = {
    name: '炫羽',
    age: 18
}

function prop<T extends object, K extends keyof T>(obj: T, key: K) {
  return obj[key];
}

const myName = prop(xuanyu, 'name') // myName: string
// const myName = prop(xuanyu, 'nickname') // 传入不存在的键值,则报错
5、infer

在条件类型语句中, infer 推断一个类型变量。

infer语法的限制如下:

  • infer只能在条件类型的 extends 子句中使用
  • infer得到的类型只能在true语句中使用, 即X中使用
//1.简单使用,该语句仅仅展示用法,无实际意义
type InferSelf<T> = T extends infer U ? U : never;  

type T1 = InferSelf<String>
let a:T1 = "aaa"
type T2 = InferSelf<number>
let b:T2 = 3

//2.推断数组(或者元组)的类型
type InferArray<T> = T extends (infer U)[] ? U : never;
type I0 = InferArray<[number, string]>; // string | number
type I1 = InferArray<string[]>; // string
type I2 = InferArray<number[]>; // number


//3.推断数组(或者元组)第一个元素的类型
type InferFirst<T extends unknown[]> = T extends [infer P, ...infer _] ? P : never
type I3 = InferFirst<[3, 2, 1]>; // 3


//4.推断函数返回值的类型
type InferReturn<T> = T extends () => infer U ? U : never

function fn(): number {
  return 20
}

type T3 = InferReturn<typeof fn>
let c:T3 = 12

六、ts 内置工具类型

TS 内置了一些类型的操作符,方便我们快速的操作类型。首字母都是大写。

1、Partial

将所有属性都变成可选

interface User {
    id: number
    name?: string
    status: number
    super: boolean
}

type Coder = Partial<User>
// Coder 的类型变成
// type Coder = {
//     id?: number | undefined;
//     name?: string | undefined;
//     status?: number | undefined;
//     super?: boolean | undefined;
// }

const lisi:  Coder = {}

注意的是,Partial 只能处理一层的数据,当有嵌套的二层数据不会进行处理。

2、Required

和 Partial 相反,Required是将所有属性变成必选。

3、Readonly

将所有属性变成只读。

4、Pick

  • 定义
type Pick<T, K extends keyof T> = { [P in K]: T[P]; }

可见需要传入两个参数。如以下例子:

interface User {
    id: number
    readonly name?: string
    status: number
    super?: boolean
}

type Coder = Pick<User, "id">
// Coder 的类型变成
// type Coder = {
//     id: number;
// }

// 必传 id 属性
const lisi:  Coder = {id: 1}

5、ReturnType

获取函数返回值的类型。

type getMoney = () => number
// 上面的函数返回值是 number
const money: ReturnType<getMoney> = 1000000 

6、Record

批量赋予某个类型。

interface User {
    id: number
    readonly name?: string
    status: number
    super?: boolean
}

type Leader = "zhangsan" | "lisi" | "wangwu" 

type Coder = Record<Leader, User>
// Coder 的类型变成
// type Coder = {
//     zhangsan: User;
//     lisi: User;
//     wangwu: User;
// }

7、Exclude

将 某个类型 从类型集中剔除。

type ManyTypes = boolean | string | number

type newTypes = Exclude<ManyTypes, boolean>
// 剩下类型
// type newTypes = string | number

8、Extract

和 Exclude 相反,Extract从类型集中提取某个类型

type ManyTypes = boolean | string | number

type newTypes = Extract<ManyTypes, boolean>
// 剩下类型
// type newTypes = boolean

9、Omit

剔除某个属性,剩余的组建新的类型。

interface User {
    id: number
    readonly name?: string
    status: number
    super?: boolean
}

type Coder = Omit<User, "status">
// Omit 剔除 status 属性 剩余以下属性
// type Coder = {
//     id: number;
//     readonly name?: string | undefined;
//     super?: boolean | undefined;
// }

10、NonNullable

用来过滤类型中的 null 及 undefined 类型。

type Name = string | undefined | null
type UserName = NonNullable<Name> // UserName 的类型为 string

11、Parameters

用于获得函数的参数类型组成的元组类型。

type Param = Parameters<(name: string, age: number) => void>
// 类型 Param 为: type Param = [name: string, age: number]

七、tsconfig.json介绍

tsconfig.json 是 TypeScript 项目的配置文件。
命令 tsc xxx.ts 通过配置文件编译出相应的js代码。

1、tsconfig.json 重要字段

  • files - 设置要编译的文件的名称;
  • include - 设置需要进行编译的文件,支持路径模式匹配;
  • exclude - 设置无需进行编译的文件,支持路径模式匹配;
  • compilerOptions - 设置与编译流程相关的选项。

2、compilerOptions 选项

{
  "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          // 为装饰器提供元数据的支持
  }
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AI_潜行者

赐予我力量吧

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值