typescript 语法简介

206 阅读6分钟

typescript 是由微软开源的编程语言。它是 JavaScript 的一个超集,而且本质上向这个语言添加了可选的静态类型和基于类的面向对象编程。

包含 js 支持的所有数据类型

  • string
  • number
  • boolean
  • array
  • symbol
  • null
  • undefined
  • bigInt
  • object

ts 特有的数据类型

  • enum // 枚举
  • 元组 // 不可变的数组
  • never
  • void
  • any
  • unknown
  • Object
  • {} // 空类型

类型声明 和 推导

// 显式声明变量的类型
//
let age: number;
age = 10;
age = true; // 不能将类型“boolean”分配给类型“number”

// 不声明变量类型,根据赋值的类型 推导出变量的类型
let height;
height = 120; // ok
height = true; // ok

let name = "string";
name = true; // 不能将类型“boolean”分配给类型“string”

/**
 *  区别:
 *    显式声明 在 变量声明的时候已经确定了变量的类型 后续不可更改
 *    类型推导 在 变量声明的时候 类型为any,赋值的时候才确定类型
 */

字面量类型

type flag = 0 | 1;
let num: flag = 0;
num = 1;
num = 2; // Error

type keyType = "name" | "age" | "class";
let str: keyType;

// 字面量类型 即定义了变量的类型 也限定了 取值

联合类型

  • 使用联合类型 读取属性 只能满足 定义 类型中的一个
type A = string | number;

let a: A = 1;
a = "1";
a = true; // Error

交叉类型

  • 使用交叉类型 读取属性 可以获取所有类型的所有属性名,但是在赋值的时候,也必须满足所有类型
  • ** 当多个类型中 存在属性类型 或者 类型 冲突 则此交叉类型会被认定为 never 类型
interface A {
  name: string; // name 类型冲突
  age: number;
}
interface B {
  name: boolean; // name 类型冲突
  sex: number;
}

type C = A & B; // never

let c: C;
c.name = "1"; // 由于属性“name”在某些要素中具有存在冲突的类型,因此已将交集“C”缩减为“绝不”

type D = string & number; // never

null undefined 数据类型

  • 只有 null any unknown 可以接受 null 值
  • 只有 undefined any unknown 可以接受 undefined 值
// 只有 null any unknown 可以接受 null 值
let n: null = null;
let nn: any = null;
let nnn: unknown = null;
// let dn: Object = null // 不能将类型“null”分配给类型“Object”
// let dn: {} = null; // 不能将类型“null”分配给类型“{}”
// 只有 undefined any unknown 可以接受 undefined 值
let n: undefined = undefined;
let nn: any = undefined;
let nnn: unknown = undefined;
// let dn: Object = undefined // undefined
// let dn: {} = undefined; // undefined{}”

never 数据类型

  • 即不存在的值的类型
  • never 类型仅能被赋值给另外一个 never
  • 应用场景:
// 应用场景
type CreateOrderType = number | boolean
function genOrder(ordType: CreateOrderType) {
    if (typeof ordType === "number") {
        // do something
    } else if (typeof ordType === "boolean") {
        // do something
    } else {
        // type CreateOrderType = number | boolean
        let d = ordType // never
        
        // type CreateOrderType = number | boolean | string
        let d2 = ordType // string
    }
}


void (与 never 区别)

  • void 表示没有任何类型
  • 当一个函数返回空值时,它的返回值为 void 类型,但是,当一个函数永不返回时(或者总是抛出错误),它的返回值为 never 类型。
  • void 类型可以被赋值(在 strictNullChecking 为 false 时),但是除了 never 本身以外,其他任何类型不能赋值给 never。

object, Object, {}的 区别

  • object 类型表示非原始类型, string number boolean symbol null 和 undefined 在 js 中为原始类型
  • Object 类型是所有 Object 类的实例类型, 包含了 js 原始的所有公共功能
  • {} 空对象,它描述了一个没有成员的对象
// object
// object 类型表示非原始类型
// string number boolean symbol null 和 undefined 在 js 中为原始类型
declare function create(data: object | null): void;
create({ name: "" }); // OK
create([1]); // OK
create(null); // OK
create(0); // Error 类型“number”的参数不能赋给类型“object”的参数
create(true); // Error
create(""); // Error
// Object
// Object 类型是所有Object类的实例类型
// 包含了 js 原始的所有公共功能

// ** 除了 null undefined 都可以赋值 **

let a: Object = 5;
a = {};
a = Symbol("5");
a = null; // Error 不能将类型 null 赋值给 Object
// {}
// 空对象,它描述了一个没有成员的对象
// 当你试图访问这样一个对象的任意属性时,TypeScript 会产生一个编译时错误。

let a: {} = 5;
a = {};
a = Symbol("5");
a = null; // Error 不能将类型“null”分配给类型“{}”

枚举

数字类型枚举

  • 数字类型枚举,允许我们将数字类型或者其他任何与数字类型兼容的类型赋值给枚举类型的实例。
  • 双向映射
enum AuditStatus {
  SUCCESS = 0, 
  FAIL,
  RETURN,
}
// 数字枚举 双向映射
console.log(AuditStatus.SUCCESS); // 0
console.log(AuditStatus["SUCCESS"]); // 0
console.log(AuditStatus[0]); // SUCCESS

let a = AuditStatus.SUCCESS;
a = 0; // 有效

编译后的 js

var AuditStatus;
(function (AuditStatus) {
  AuditStatus[(AuditStatus["SUCCESS"] = 0)] = "SUCCESS";
  AuditStatus[(AuditStatus["FAIL"] = 1)] = "FAIL";
  AuditStatus[(AuditStatus["RETURN"] = 2)] = "RETURN";
})(AuditStatus || (AuditStatus = {}));

let a = AuditStatus.SUCCESS; // 与常量枚举的差异
a = 0; // 有效

字符串枚举

枚举类型的值,也可以是字符串类型。

// 字符串枚举
enum WorkStatus {
  TOP = "AM",
  BOTTOM = "PM",
}

// 字符串枚举 单项关联
console.log(WorkStatus.TOP); // AM
console.log(WorkStatus["BOTTOM"]); // PM
// console.log(WorkStatus["PM"]) // Error

编译后的 js

// 字符串枚举
var WorkStatus;
(function (WorkStatus) {
  WorkStatus["TOP"] = "AM";
  WorkStatus["BOTTOM"] = "PM";
})(WorkStatus || (WorkStatus = {}));

常量枚举

// 常量枚举
enum AuditStatus {
  SUCCESS = 0,
  FAIL,
  RETURN,
}

const status = AuditStatus.SUCCESS;
console.log(status);

编译后的 js

const status = 0; /* AuditStatus.SUCCESS */

any 和 unknown 类型

  • 相同点:
    • 任意类型的变量都可以赋值给 any 或者 unknown
  • 不同点
    • any 类型的变量可以赋值给 其他类型的变量, unknown 不行
    • 不能用 unknown 类型的变量来获取任何属性和方法,但是 any 类型的变量可以获取任意名称的属性和任意名称的方法

总结:unknown 类型是安全的 any,unknown 的变量不能直接赋值给其他变量

let num: number = 2;
let data: any = num;

let num2: any = "str";
let data2: number = num2;

// unknown 类型安全的any,unknown的变量不能直接赋值给其他变量
let num3: unknown = 5;
// let data3: string = num3  // Error

interface 接口

  • 接口可以继承 (extends)
  • 类可以实现接口 (implements)
interface Product {
  id: number;
  name: string;
}

interface Pet {
  name: string;
  love: number;
}

// ** 继承
interface Dog extends Pet {
  strain: number;
}

let apple: Product = {
  id: 1,
  name: "Apply red",
};

// **类可以实现 interface
interface List {
  add(): void;
  remove(): void;
}

class ArrayList implements List {
  add(): void {
    throw new Error("Method not implemented.");
  }
  remove(): void {
    throw new Error("Method not implemented.");
  }
}

class LinkedList implements List {
  add(): void {
    throw new Error("Method not implemented.");
  }
  remove(): void {
    throw new Error("Method not implemented.");
  }
}

索引签名

type Index = "a" | "b" | "c";
type FromIndex = { [k in Index]?: number };

const good: FromIndex = { b: 1, c: 2 };

// Error:
// `{ b: 1, c: 2, d: 3 }` 不能分配给 'FromIndex'
// 对象字面量只能指定已知类型,'d' 不存在 'FromIndex' 类型上
const bad: FromIndex = { b: 1, c: 2, d: 3 };

interface Product {
  name: string;
  price: number;
  stock: number;
  [x: string]: any; // any 为了 兼容已定义的所有类型
  // [x: string]: number // Error 因为 name:string 类型不匹配
}

// [x: string]: any =>  ok
let p: Product = {
  name: "Apple",
  price: 15.5,
  stock: 1000,
  desc: "desc",
  100: 125,
};

interface Product2 {
  name: string;
  price: number;
  stock: number;
  // [x: number]: any; // any 为了 兼容已定义的所有类型
  [x: string]: number; // Error 因为 name:string 类型不匹配
}

let p2: Product2 = {
  name: "Apple",
  price: 15.5,
  stock: 1000,
  // desc: "desc", // Error
  100: 125,
};

// 索引访问类型
interface Person {
  name: string;
  age: number;
}

type A = Person["age"]; // type A = number
type B = Person["name"]; // type B = string


type pKeys = keyof Person; // "name" | "age" 字面量类型
let pk: pKeys = "name";


元组

定义:

  • 在定义时,每个元素的类型都确定
  • 元素值的类型必须是当前元素定义的类型
  • 元素值的个数必须和定义时个数相同

总结:长度固定 类型固定的数组 (可变元组 可变部分除外)

let salary: [string, number, number, string, number] = [
  "张三",
  5,
  15,
  "ok",
  120,
];

可变元组

// 可变元组

type Customer = [string, number, string, ...any[]];
let List: Customer = ["张三", 18, "小学"];
let List2: Customer = ["张三", 18, "小学", 15];
let List3: Customer = ["张三", 18, "小学", {}];

// 解构
const [cName, cAge, cAddress, ...rest] = List;

元组 标签 tag

// 可变元组 tag

type Customer = [name: string, age: number, addr: string, ...rest: any[]];
let List: Customer = ["张三", 18, "庞各庄"];
let List2: Customer = ["张三", 18, "庞各庄", 15];
let List3: Customer = ["张三", 18, "庞各庄", {}];

const [cName, cAge, cAddress, ...rest] = List;