# 基础

# 类型

给变量提供一个确定的类型叫做类型注解

# 基本类型

// number
let tn: number = 1;
let tn1: number = 0xf00d;
// string
let ts: string = "1";
// boolean
let tb: boolean = true;
// symbol
let ts: symbol = Symbol("1");
// null
let tnull: null = null;
// undefined
let tu: undefined = undefined;
// never
// 没有类型可以分配
function error(message: string): never {
  throw new Error(message);
}
// never 可以分配给其他类型
// 其他类型不能分配给never类型
type Bool = never extends "a" ? string : number; // string

strictNullChecks 可以设置nullundefined是否可以赋值给基础类型

# 数组

let ta: number[] = [1, 2];
let ta1: Array<number> = [1, 2]; // 通过定义的接口interface Array
// 联合类型数组,一般通过别名来定义联合类型
let ta2: number | string = [1, 2, "1"];

# 元组

确定长度的数组,类型也必须确定

let tuple: [string, number] = ["str", 111];
console.log(tuple[2]); // 会直接报错

可变元祖

let c: [number, ...any[]] = [2, "a", 3, "b"];

# 特殊类型

# any

// 任意类型
let no: any = 4;
no = "1";
no = [1];

# void

// 没有返回值
function v(): void {
  console.log(1);
}

# enum

// 默认从0开始
enum Color {
  red,
  green,
}

// 从1开始
enum Color1 {
  red = 1,
  green,
}

const c: Color = Color.red; // 0

# 对象

非原始类型

const obj: object = { a: 1 };

# 类型推断

如果不主动定义类型,则会根据定义自动推断出类型

let tnumber = 1;

# 类型断言

强制转化为对应的类型

// 尖括号
function fn(val: any) {
  console.log((<Array<number>>val).slice());
}

const inputValue = (<HTMLInputElement>oInput).value;

// as
function fn(val: any) {
  console.log((val as Array<number>).slice());
}

fn([1, 2]);

# 非空断言

  • ! 非空断言,一定是存在的
  • ?表示可能存在,可能不存在,一种语法糖,会编译成三元。
  • 可以用在函数参数,接口定义等定义/使用变量的地方
function f(data?: string) {
  const a = data!.toString();
  console.log(a);
}

# 常量断言

as const 作为关键词 ,禁止修改

const aaa = 1 as const; // 禁止修改

# 接口

定义对象,类,函数的结构信息

interface People {
  name: string;
  age?: number; // 可选属性
  readonly sex: number;
  // 任意属性
  [propName: string]: any;
}
const people: People = { name: "john", age: 12, sex: 1 };
people.sex = 2; // 报错,只读的数据不能修改

// 两个interface会合并
interface People {
  phone: number;
}

# 接口继承

通过extends关键词来继承,继承后必须都满足定义的类型

interface IPeople {
  id: number;
}

interface IChilren extends IPeople {
  name: string;
  age: number;
}

# 类型别名

可以为任何类型注解提供类型别名,一般用作联合类型和交叉类型

type Cb = () => void;
// 联合类型,只能取一
type NewType = string | number;

// 交叉类型,同时满足
type o1 = { a: string };
type o2 = { b: string };
let o1: o1 = { a: "a" };
let o2: o2 = { b: "b" };
let o3: o1 & o2 = { a: "a", b: "b" };

// 字面量类型
type name = "john" | "jack";
// 联合转化为交叉类型
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
  k: infer I
) => void
  ? I
  : never;

type I = UnionToIntersection<{ a: string } | { b: string }>; // {a:string} & {b:string}
// U extends any 的意义在于,遍历每一个联合类型的成员处理
// ((k:{a:string}) => void )| ((k:{b:string}) => void )
// 之后,能同时约束以上条件的I,为上面的父级(k: { b: number } & { a: string }) => void

// T[number] ===  o1 | o2 | o3
function cross<T extends object[]>(...args: T): UnionToIntersection<T[number]> {
  return Object.assign({}, ...args);
}

const oAll = cross(oo1, oo2, oo3);
console.log(oAll);

# 函数

// 原则上函数参数都需要定义参数类型
// 函数返回值一般不需要定义类型,会自动推断,除非是泛型
// 参数可以传递默认值,
function add(x: number, y: number, z: number = 1): number {
  return x + y + z;
}

# 剩余参数

function getRest(a: number, ...rest: number[]) {
  console.log(a, rest);
}

getRest(1, 2, 3); // 1,[2,3]

# 在接口中定义函数

interface P {
  fn(x: number): number;
  fn2: (x: number) => number;
}

# 重载

为了表义更加清晰

// 函数重载
// 重载签名,没有函数体,不需要实现,可以有多个
function reload(n: number): number;
function reload(s: string): string;
// 实现签名
function reload(c: number | string) {
  return c;
}

#

class People {
  name: string;
  constructor(name: string) {
    this.name = name;
  }
  say(word: string) {
    console.log(this.name + "say " + word);
  }
}
const p = new People("john");

ts中,默认成员均为public,同样也可以定义为private,protected

# 泛型

定义时不明确,使用时明确的类型,也会进行类型检查

interface Ref<T> {
  value: T;
}
type S = { name: string; age: number };
const ref: Ref<S> = {
  value: { name: "111", age: 11 },
};

ref.value.name; // 定义ref的时候传入了S,所以ref.value就会推导出S的结构

# 参数要求一致

function add<T extends number | string>(a: T, b: T) {
  console.log(a, b);
}

add(1, 2);
add("1", "2");
add(1, "2"); // 报错

# 泛型默认值(2.3+)

function add<T = number>(a: T, b: T): T {
  console.log(a, b);
}

# 泛型约束

通过extends关键字来约束泛型

interface PName {
  name: string;
}
// T被约束为一个对象,带有name属性
function createName<T extends PName>(arg: T): T {
  return arg;
}
createName({ name: "john" }); //  限制必须传入对象包含name属性

# 泛型联合类型

如果是泛型联合类型,会进行分配,和单独使用 extends 不一样

type TBool<T> = T extends "a" ? string : number;
type TBool1 = "a" | "b" extends "a" ? string : number; // number
type TBool2 = TBool<"a" | "b">; // string | number
// never作为泛型传入就是never
type TBool3 = TBool<never>; // never

# 声明文件

主要是对类型定义的文件.d.ts,需要在tsconfiginclude引入。

  • 编写的 ts 文件会自动生成声明文件。
  • 加载第三方库的时候需要声明文件来获取类型的提示,有些库自动集成,有些需要手动安装@types

# 定义声明文件

声明文件的定义必须通过declare,可以通配置skipCheckLib取消,一般不建议。

 declare let a; //  声明全局变量
 declare const b; // 声明全局变量
 declare function // 声明全局方法
 declare class // 声明全局类
 declare enum // 声明全局枚举
 declare namespace  // 声明(包含子属性)全局对象
//  interface/type 可以不写declare

# 定义命名空间,防止定义重名

declare namespace JQuery {
  function $(): void;
  function $(selector: string): void;
}
// 使用
JQuery.$();

# 模块声明

declare module JQueryModule {
  function $(n: number): void;
  namespace $ {
    function css(): void;
  }
  // export default $; // 可以用,但是不推荐
  export = $;
}

使用

import $ from "./JQueryModule";
$.css();