TypeScript-学习ts基础

93 阅读8分钟

介绍

  • 以js为基础构建的语言、一个js的超集、ts扩展了js并添加了类型、可以在任何支持js的平台中执行、ts不能被js解析器直接执行。

  • js开发速度快,后期不便于维护,报错问题不便于分析定位;ts开发中需定义好变量类型,有利于后期维护,其他开发人员解读。

  • Js变量是动态类型 例:let a = 10, a = ‘hi’, a = true; js:let a = 'b'; a = 1; a = true; a = [1, 'b', false]✔️ 可给定义变量赋任意类型值

ts:let t: string = 'b'; t = 2; t = false;✖️ 仅支持赋给变量规定类型的值

  • 基本类型 布尔值: boolean 数值: number 字符串: string Null Undefined undefined 和 null 是所有类型的子类型。

数组 元组 枚举 any unkonwn viod 没有返回值 never 不会出现的值

安装 ts-node

那么通过我们上面的一通操作,我们知道了运行tsc命令就可以编译生成一个js文件,但是如果每次改动我们都要手动去执行编译,然后再通过 node命令才能查看运行结果岂不是太麻烦了。

而 ts-node 正是来解决这个问题的

npm i -g ts-node // 全局安装ts-node

有了这个插件,我们就可以直接运行.ts文件了

我们试一下

ts-node helloworld.ts

可以看到我们的打印结果已经输出

后续我们的示例都可以通过这个命令来进行验证

接下来我们就可以正式进入到 typescript 的学习之旅了

TypeScript 基础类型

Boolean 类型

const flag: boolean = true;

Number 类型

const count: number = 10;

String 类型

  let name: string = "树哥";

Enum 类型

枚举类型用于定义数值集合,使用枚举我们可以定义一些带名字的常量。使用枚举可以清晰地表达意图或创建一组有区别的用例。,如周一到周日,方位上下左右等

  • 普通枚举

初始值默认为 0 其余的成员会会按顺序自动增长 可以理解为数组下标

enum Color {
  RED,
  PINK,
  BLUE,
}

const red: Color = Color.RED;
console.log(red); // 0

  • 设置初始值
enum Color {
  RED = 2,
  PINK,
  BLUE,
}
const pink: Color = Color.PINK;
console.log(pink); // 3

  • 字符串枚举
enum Color {
  RED = "红色",
  PINK = "粉色",
  BLUE = "蓝色",
}

const pink: Color = Color.PINK;
console.log(pink); // 粉色

  • 常量枚举

使用 const 关键字修饰的枚举,常量枚举与普通枚举的区别是,整个枚举会在编译阶段被删除 我们可以看下编译之后的效果

const enum Color {
  RED,
  PINK,
  BLUE,
}

const colorColor[] = [Color.REDColor.PINKColor.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)类型

上面数组类型的方式,只能定义出内部全为同种类型的数组。对于内部不同类型的数组可以使用元组类型来定义

元组( Tuple )表示一个已知数量和类型的数组,可以理解为他是一种特殊的数组

  const tuple: [numberstring] = [1"zhangmazi"];

需要注意的是,元组类型只能表示一个已知元素数量和类型的数组,长度已指定,越界访问会提示错误。例如,一个数组中可能有多种类型,数量和类型都不确定,那就直接any[]。

undefined和null

默认情况下 null 和 undefined 是所有类型的子类型。也就是说你可以把 null 和 undefined 赋值给其他类型。

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

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

如果你在tsconfig.json指定了"strictNullChecks":true ,即开启严格模式后, null 和 undefined 只能赋值给 void 和它们各自的类型。

// 启用 --strictNullChecks
let x: number;
x = 1; // 编译正确
x = undefined;    // 编译错误
x = null;    // 编译错误

any 类型

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

  let value: any = 1;
  value = "zhangmazi"; // 编译正确
  value = []; // 编译正确
  value = {};// 编译正确

void 类型

void 意思就是无效的, 一般只用在函数上,告诉别人这个函数没有返回值。

  function sayHello(): void {
    console.log("hello 啊,树哥!");
  }

never 类型

never 类型表示的是那些永不存在的值的类型。例如never 类型是那些总是会抛出异常或根本就不会有返回值的函数表达式或箭头函数表达式的返回值类型

值会永不存在的两种情况:

  • 1 如果一个函数执行时抛出了异常,那么这个函数永远不存在返回值(因为抛出异常会直接中断程序运行,这使得程序运行不到返回值那一步,即具有不可达的终点,也就永不存在返回了)
  • 2 函数中执行无限循环的代码(死循环),使得程序永远无法运行到函数返回值那一步,永不存在返回。
// 异常
function error(msg: string): never { // 编译正确
  throw new Error(msg); 
}

// 死循环
function loopForever(): never { // 编译正确
  while (true) {};
}

Unknown 类型

unknown与any一样,所有类型都可以分配给unknown:

  let value: unknown = 1;
  value = "zhangmazi"; // 编译正确
  value = false; // 编译正确

unknown与any的最大区别是:

任何类型的值可以赋值给any,同时any类型的值也可以赋值给任何类型。unknown 任何类型的值都可以赋值给它,但它只能赋值给unknown和any

对象类型

这里所说的对象类型,就是我们常说的函数、{}、数组、类

object, 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

大 Object 代表所有拥有 toString、hasOwnProperty 方法的类型 所以所有原始类型、非原始类型都可以赋给 Object(严格模式下 null 和 undefined 不可以)

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

  • {}

{} 空对象类型和大 Object 一样 也是表示原始类型和非原始类型的集合

在 TypeScript 中,我们通过 Class 关键字来定义一个类

class Person {
  namestring;
  agenumber;
  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];

函数

函数声明

function add(x: number, y: number): number {
  return x + y;
}

函数表达式

const add = function(x: number, y: number): number {
  return x + y;
}

接口定义函数

interface Add {
  (xnumberynumber): number;
}

可选参数

function add(x: number, y?: number): number {
  return y ? x + y : x;
}

默认参数

function add(x: number, y: number = 0): number {
  return x + y;
}

剩余参数

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

函数重载

函数重载或方法重载是使用相同名称和不同参数数量或类型创建多个方法的一种能力。

function add(x: number, y: number): number;
function add(x: string, y: string): string;
function add(x: any, y: any): any {
  return x + y;
}

上面示例中,我们给同一个函数提供多个函数类型定义,从而实现函数的重载

需要注意的是:

函数重载真正执行的是同名函数最后定义的函数体 在最后一个函数体定义之前全都属于函数类型定义 不能写具体的函数实现方法 只能定义类型

类型推论

如果没有明确的指定类型,那么 TypeScript 会依照类型推论的规则推断出一个类型。

let x = 1;
x = true; // 报错

上面的代码等价于

let x: number = 1;
x = true; // 报错

通过上述示例我们可以看出,我们没有给 x 指定明确类型的时候,typescript 会推断出 x 的类型是 number。

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

let x;
x = 1; // 编译正确
x = true; // 编译正确

类型断言

某些情况下,我们可能比typescript更加清楚的知道某个变量的类型,所以我们可能希望手动指定一个值的类型

类型断言有两种方式

  • 尖括号写法
let str: any = "to be or not to be";
let strLength: number = (<string>str).length;

  • as 写法
let str: any = "to be or not to be";
let strLength: number = (str as string).length;

非空断言

在上下文中当类型检查器无法断定类型时,可以使用缀表达式操作符 ! 进行断言操作对象是非 null 和非 undefined 的类型,即x!的值不会为 null 或 undefined

  let userstring | null | undefined;
  console.log(user!.toUpperCase()); // 编译正确
  console.log(user.toUpperCase()); // 错误

确定赋值断言

let value:number
console.log(value); // Variable 'value' is used before being assigned.

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

通过 let x!: number; 确定赋值断言,TypeScript 编译器就会知道该属性会被明确地赋值。

let value!:number
console.log(value); // undefined 编译正确

联合类型

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

let status:string|number
status='to be or not to be'
status=1

类型别名

类型别名用来给一个类型起个新名字。它只是起了一个新名字,并没有创建新类型。类型别名常用于联合类型。

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

交叉类型

交叉类型就是跟联合类型相反,用&操作符表示,交叉类型就是两个类型必须存在

interface IpersonA{
  namestring,
  agenumber
}
interface IpersonB {
  namestring,
  genderstring
}

let personIpersonA & IpersonB = { 
    name"师爷",
    age18,
    gender"男"
};

person 即是 IpersonA 类型,又是 IpersonB 类型

注意:交叉类型取的多个类型的并集,但是如果key相同但是类型不同,则该key为never类型

interface IpersonA {
    name: string
}

interface IpersonB {
    name: number
}

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

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