TypeScript 入门学习二 — TypeScript 类型系统 一

457 阅读10分钟

image.png

前面已经学习了解了 TypeScript 是什么以及 TypeScript 与 JavaScript 的特点

下面我们开始正式学习 TypeScript

TypeScript 开发环境的搭建

安装 Node 运行环境

如果已经安装了,可以在命令行工具上使用。node -v 查看已经安装的版本

如果电脑上需要管理多版本的 node 版本,推荐使用 nvm 进行管理, 具体安装方法(如果安装出现问题,推荐百度大法~)

nvm ls 查看所有的 node 版本

image.png

nvm use 12.22.1  切换 node 版本

image.png

全局安装 typescriptts-node

使用 npm 或者 yarn 全局安装

npm install typescript -g
npm install ts-node -g
yarn  global  add typescript 

ps: 使用 ts-node 插件解决每次开发完需要编译之后再看结果的问题

建立项目目录 & 编译 ts 文件

在文件目录下新建一个 demo.ts 文件,写下如下代码:

function greeter(person: string) {
    return "Hello, " + person;
}

let user = "Daisy";

console.log(greeter(user));

执行编译命令 tsc demo.ts, 会看到生成一个 demo.js 文件

tsc demo.ts

编译之后的代码, demo.js 如下:

function greeter(person) {
    return "Hello, " + person;
}
var user = "Daisy";
console.log(greeter(user));

Run Code 执行 demo.js 输出:

image.png

如果不想每次修改代码之后编译一次,可以使用 ts-node 这个插件来解决;

ts-node demo.ts

image.png

ps: 这里需要注意,执行 ts-node 之前需要生成 tsconfig.js 配置文件(在没有生成配置文件之前执行会报错)

如果需要每次让 ts 自动编译成 js ,可执行以下命令:

tsc --watch demo.ts

这样每次在 ts 文件修改之后,我们 js 文件会自动编译

image.png

TypeScript 类型系统

TypeScript 的一个最主要特点就是可以定义静态类型,英文是 Static Typing;简单的理解“静态类型”为,就是你一旦定义了,就不可以再改变;

基本注解(如何定义静态类型)

类型注解使用 :TypeAnnotation 语法。在类型声明空间中可用的任何内容都可以用作类型注解。

const num:number = 123

这里 :number 就是定义一个静态类型,定义之后这个变量在程序中就是数字类型,不可改变,一旦改变就会报错

const num:number = 123
num = '18'  // 会报错

image.png

自定义静态类型

我们可以自定义一个静态类型, 比如我们现在定义一个 obj类型,在声明变量时候就可以使用这个静态类型;

interface Obj {
    name: string,
    age: number
}

const xiaoh: Obj = {
    name: '小红'age: 18
}

如果声明的变量和定义的不一致就会报错,这时候 xiaoh 变量也具有 nameage 属性

需要记住的是:如果使用了静态类型,不仅意味着变量的类型不可以改变,还意味着类型的属性和方法也跟着确定了

这个特点就大大提高了程序的健壮性,并且编辑器这时候也会给你很好的语法提示,加快了你的开发效率

TypeScript 原始类型

JavaScript 原始类型也同样适应于 TypeScript 的类型系统,因此 stringnumberboolean 也可以被用作类型注解:

布尔类型(boolean)

let bool: boolean;

bool = true;  // or false
bool = 'false'; // Error

Number 类型

let num: number;
num = 123;
num = '123'; // Error

 String 类型

let str: string;
str = '123';
str = 123; // Error

Symbol

在使用 Symbol 的时候,必须添加 es6 的编译辅助库 需要在 tsconfig.json 的 lib 字段加上ES2015 Symbol 的值是唯一不变的

demo.ts

const sym1 = Symbol("Hello");
const sym2 = Symbol("Hello");
console.log(Symbol("Hello") === Symbol("Hello"));

tsconfig.json

"lib": ["DOM", "ES2015", "ScriptHost", "ES2019.Array"], 

image.png

image.png

特殊类型

除了被提到的一些原始类型,在 TypeScript 中,还存在一些特殊的类型,它们是 any、 null、 undefined 以及 void

any 任意类型

any 类型在 TypeScript 类型系统中占有特殊的地位。它提供给你一个类型系统的「后门」,TypeScript 将会把类型检查关闭。在类型系统里 any 能够兼容所有的类型(包括它自己)。因此,所有类型都能被赋值给它,它也能被赋值给其他任何类型。举个🌰

demo.ts

let power: any;
// 赋值任意类型
power = '123';
power = 123;

// 它也兼容任何类型
let num: number;
power = num;
num = power;

let value: any;

value.foo.bar; // OK
value.trim(); // OK
value(); // OK
new value(); // OK
value[0][1]; // OK

  • 一般使用场景: 第三方库没有提供类型文件时可以使用 any 类型转换遇到困难或者数据结构太复杂难以定义 不过不要太依赖 any 否则就失去了 ts 的意义了
  • 为了解决 any 带来的问题,TypeScript 3.0 引入了 unknown 类型

Unknown 类型

所有类型都可以赋值给 any,所有类型也都可以赋值给 unknown 这使得 unknown 成为 TypeScript 类型系统的另一种顶级类型(另一种是 any)。下面我们来看一下 unknown 类型的使用示例:

let value: unknown;

value = true; // OK
value = 42; // OK
value = "Hello World"; // OK
value = []; // OK
value = {}; // OK
value = Math.random; // OK
value = null; // OK
value = undefined; // OK
value = new TypeError(); // OK
value = Symbol("type"); // OK

对 value 变量的所有赋值都被认为是类型正确的。但是,为 unknown 的值赋值给其他类型的变量看看:

let value: unknown;

let value1: unknown = value; // OK
let value2: any = value; // OK
let value3: boolean = value; // Error
let value4: number = value; // Error
let value5: string = value; // Error
let value6: object = value; // Error
let value7: any[] = value; // Error
let value8: Function = value; // Error

unknown 类型只能被赋值给 any 类型和 unknown 类型本身,只有能够保存任意类型值的容器才能保存 unknown 类型的值

再对类型为 unknown 的值执行操作时;如对类型为 any 操作一样看看:

let value: unknown;

value.foo.bar; // Error
value.trim(); // Error
value(); // Error
new value(); // Error
value[0][1]; // Error

unknown 和 any 的主要区别:

  • unknown 和 any 的主要区别是 unknown 类型会更加严格
  • 在对 unknown 类型的值执行大多数操作之前 我们必须进行某种形式的检查
  • 在对 any 类型的值执行操作之前 我们不必进行任何检查
  • 所有类型都可以被归为 unknown 但unknown类型只能被赋值给 any 类型和 unknown 类型本身
  • 而 any 啥都能分配和被分配

null 和 undefined

在类型系统中,JavaScript 中的 nullundefined 字面量和其他被标注了 any 类型的变量一样,都能被赋值给任意类型的变量,如下例子所示:

let num: number;
let str: string;

// 这些类型能被赋予
num = null;
str = undefined;

console.log(num)
console.log(str)

image.png

注意: 如果要使用 null , undefined 能被赋值给任意类型的话, 需要在 tsconfig.json 的配置里面的 strictNullChecks 设置成 fasle,如果指定为 true, 那么 null 和 undefined 只能赋值给 void 和它们各自的类型。

tsconfig.json

"strictNullChecks": false, // 不允许把 null、undefined 赋值给其他类型变量

void

void 表示没有任何类型, 使用 :void 来表示一个函数没有返回值

function log(message: string): void {
  console.log(message);
}

never 类型

never 一般表示用户无法达到的类型 例如never 类型是那些总是会抛出异常或根本就不会有返回值的函数表达式或箭头函数表达式的返回值类型

function neverReach(): never {
  throw new Error("an error");
}

在 TypeScript 中,可以利用 never 类型的特性来实现全面性检查,具体示例如下:

type Foo = string | number;

function controlFlowAnalysisWithNever(foo: Foo) {
  if (typeof foo === "string") {
    // 这里 foo 被收窄为 string 类型
  } else if (typeof foo === "number") {
    // 这里 foo 被收窄为 number 类型
  } else {
    // foo 在这里是 never
    const check: never = foo;
  }
}

else 分支里面,我们把收窄为 neverfoo 赋值给一个显示声明的 never 变量。如果一切逻辑正确,那么这里应该能够编译通过。如果需要修改 Foo 的类型:

type Foo = string | number | boolean;
function controlFlowAnalysisWithNever(foo: Foo) {
  if (typeof foo === "string") {
    // 这里 foo 被收窄为 string 类型
  } else if (typeof foo === "number") {
    // 这里 foo 被收窄为 number 类型
  } else {
    // 如果这里没有修改那么foo 在这里是 boolean
    // 会导致无法赋值给 never 类型,这时就会产生一个编译错误
    const check: never = foo;
  }
}

通过这个示例:我们可以得出一个结论:使用 never 避免出现新增了联合类型没有对应的实现,目的就是写出类型绝对安全的代码

ps: nevervoid 的区别

  • void 可以被赋值为 nullundefined 的类型
  • never 则是一个不包含值的类型
  • 拥有 void 返回值类型的函数能正常运行
  • 拥有 never 返回值类型的函数无法正常返回,无法终止,或会抛出异常

数组类型

TypeScript 为数组提供了专用的类型语法,因此你可以很轻易的注解数组。它使用后缀 [], 接着你可以根据需要补充任何有效的类型注解(如::boolean[])。它能让你安全的使用任何有关数组的操作,而且它也能防止一些类似于赋值错误类型给成员的行为

  1. 「类型 + 方括号」表示法
let boolArray: boolean[];
boolArray = [true, false];
console.log(boolArray[0]); // true
console.log(boolArray.length); // 2

boolArray[1] = true;
boolArray = [false, false];

boolArray[0] = 'false'; // Error
boolArray = 'false'; // Error
boolArray = [true, 'false']; // Error
  1. 数组泛型
let numArray: Array<number>; //Array<number>泛型语法

numArray = [1,2,3]
console.log(numArray)
  1. 用接口(interface)表示数组
interface NumberArray {
    [index: number]: number;
}
let num2: NumberArray = [1, 1, 2, 3, 5];
console.log(num2)

元组类型(tuple)

  • 在 TypeScript 的基础类型中,元组( Tuple )表示一个已知数量类型的数组 其实可以理解为他是一种特殊的数组;
  • 一般我们知道,数组一般由同种类型的值组成,但有时我们需要在单个变量中存储不同类型的值,这时候我们就可以使用元组
  • 在 JavaScript 中是没有元组的,元组是 TypeScript 中特有的类型,其工作方式类似于数组。

1. 元组可用于定义具有有限数量的未命名属性的类型 2. 每个属性都有一个关联的类型
3. 使用元组时,必须提供每个属性的值

let tupleD: [string, number];

tupleD = ["hello", 1];

console.log(tupleD[0]); // hello
console.log(tupleD[1]); // 1

在元组初始化的时候,如果出现类型不匹配的话,比如:

tupleD = [1, "hello"];

此时,TypeScript 编译器会提示以下错误信息:

image.png

在元组初始化的时候,我们还必须提供每个属性的值,不然也会出现错误,比如:

此时,TypeScript 编译器会提示以下错误信息:

image.png

对象类型

简单理解interface 和 type 的区别:type 更强大,interface 可以进行声明合并,type 不行;
一般声明都用interface,需要用到其他变量类型,type多一些

  • 基础使用

在 TypeScript 中,我们使用接口(Interfaces)来定义对象的类型。 接口是 TypeScript 的一个核心知识,它能合并众多类型声明至一个类型声明; 简单例子:

interface Name {
  first: string;
  second: string;
}

let name: Name;
name = {
  first: 'John',
  second: 'Doe'
};

name = {
  // Error: 'Second is missing'
  first: 'John'
};

name = {
  // Error: 'Second is the wrong type'
  first: 'John',
  second: 1337
};

接口在 TS 中比较重要,这里不展开,后面专门来学习接口~

object, Object 和 {} 类型

object 类型用于表示非原始类型

let objectCase: object;
objectCase = 1; // error
objectCase = "a"; // error
objectCase = true; // error
objectCase = null; // error
objectCase = undefined; // error
objectCase = {}; // ok

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

let ObjectCase: Object;
ObjectCase = 1; // ok
ObjectCase = "a"; // ok
ObjectCase = true; // ok
ObjectCase = null; // error
ObjectCase = undefined; // error
ObjectCase = {}; // ok

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

let simpleCase: {};
simpleCase = 1; // ok
simpleCase = "a"; // ok
simpleCase = true; // ok
simpleCase = null; // error
simpleCase = undefined; // error
simpleCase = {}; // ok

小结

关于 TS 的类型系统就到这里了, 后面还有 交叉类型, 联合类型,泛型,类型保护,类型断言,类型别名 会单独再学习~

关于 TS 的知识点还挺多的,查阅了很多资料~

参考文章

最全的TypeScript学习指南
技术胖的 TypeScript免费视频图文教程(2W字)
TS的对象类型、数组类型、函数类型 掌握 tsconfig.json
深入理解 TypeScript
1.2W字 | 了不起的 TypeScript 入门教程

TS 推荐学习网站

TypeScript 中文手册
技术胖 - TypeScript 从入门到精通图文视频教程-免费教程
深入理解 TypeScript
TypeScript 官方提供的在线 TypeScript 运行环境

如果大佬还知道更多的学习网站,欢迎留言~