初始TypeScript,认识类型和类型操作

553 阅读7分钟

1. 前言

上一篇文章,我们初步认识了TypeScript,了解了一些概念和特点,以及和javascript的区别,还简易的搭建了分别在nodejs上和浏览器上的开发环境。那么这篇文章,我们接着学习TypeScript的数据类型和类型操作部分。由于之前我并没有TypeScript的开发经验,所以把学习的TypeScript的整个过程记录下来,这将是一个系列。一方面是记录学习的过程,方便随时查阅;另一方面是一点一点分享出来,希望能给想要学习TypeScript的同学一些帮助。

如果想从0开始学习TypeScript的同学,极力推荐我之前的写的文章:

第一篇 -- 邂逅TypeScript,搭建简易ts开发环境 - 掘金

第二篇 -- 初始TypeScript,认识类型和类型操作

第三篇 -- 深入TypeScript, 函数详解

学习本文,你将学到:

1. javascript 和 TypeScript 相同的类型
2. TypeScript独有的类型
3. unknown 类型和 any 类型的区别
4. 联合类型、元组类型结合类型别名使用
5. 可选类型和联合类型的区别
6. 等等

2. TypeScript类型

在学习TypeScript语法过程中,可以使用我的 [上篇文章搭建的ts开发环境](邂逅TypeScript,搭建简易ts开发环境 - 掘金), 也可以使用官方提供的 Playground

2.1 string 类型

let name: string = 'hsh';
// ES5: var str1 = 'hsh';

2.2 number 类型

let num: number = 100;
// ES5: var num = 100;

2.3 boolean 类型

let flag: boolean = true;
// ES5: var flag = true;

2.4 Array 类型

Array类型由两种写法:

第一种:

// 第一种写法 不推荐(在jsx语法中可能会有问题,当然也是有方法可以解决的)
const names: Array<string> = []
names.push('123')
// ES5:var names = []; names.push('123');

第二种:

// 推荐使用这种方式
const names: number[] = []
types.push(123) 
// ES5:var names = []; names.push(123);

2.5 null 和 undefined 类型

let n: null = null;
// ES5:var n = null;

let u: undefined = undefined;
// ES5: var u = undefined;

2.6 any 类型

TypeScript中的一个特殊的类型,任何类型都可以被归为 any 类型。any类型可以被任何类型赋值:

let info: any = 'Hello world'
info = 123
info = false

TypeScript 允许我们对 any 类型的值执行任何操作(调用方法、访问属性等),而无需事先执行任何形式的检查。比如:

let info: any
info.name // ok
info.get() // ok
info[1] // ok

这看起来和写javascript代码并没有什么区别,几乎没有什么限制。但是对开发人员来说,这是很危险的,因为很容易写出在运行时出问题的代码,也失去了类型检查的意义。为了解决 any 带来的问题,所以在TypeScript3.0后,新增了unknown类型。

2.7 unknown 类型

unknown类型和any类型一样,可以被任意类型赋值:

let un: unknown
un = false; // ok
un = '123'; // ok
un = ['123'] // ok
un = 123 // ok
un = {
  name: 'hsh'
} // ok
un = () => {} // ok
un = null // ok
un = undefined // ok

既然unknown类型是为了解决any类型带来的问题,那么两者肯定是有所区别的。我们尝试把unknown类型的值赋值给其他类型,可以得知结果如下:

let un: unknown
let value1: string = un // Error
let value2: number = un // Error
let value3: boolean = un // Error
let value4: string[] = un // Error

let value5: any = un // ok
let value6: unknown = un // ok

从结果可以很明显的看出来,当把unknown类型赋值给stringnumberboolean等类型的时候,都是报错的,只有赋值给anyunknown类型的时候是正常的;也就是说unknown类型只能赋值给any类型和unknown类型本身,这也不难理解,因为unknown类型不能确定自己存储的是什么类型,如果可以赋值给string等类型的话,那么类型检测将变得不可控。

然后我们再看看如果对unknown类型的值做一些操作会发生什么:

let un: unknown
un()  // Error
un[1]  // Error
un.name  // Error
un.get()  // Error

可以看到,将变量un设为unknown类型后,这些操作都不再被认为是类型正确的。所以在开发过程中,当不确定是什么类型时,最好用unknown类型,而少用any类型。

2.8 void 类型

void类型是指当一个函数没有返回值时,它就是void类型。

// ts
function foo(name: string): void {
    console.log(name)
}
foo('hsh')


// ES5
function foo(name) {
    console.log(name);
}
foo('hsh');

但是我们知道,函数没有明确返回值时,其实返回的是undefined,所以void类型是可以被undefined类型赋值的:

let value: void
value = undefined // ok

2.9 never 类型

never 类型是 TypeScript 中的底层类型。never 类型是那些总是会抛出异常或根本就不会有返回值的函数表达式或箭头函数表达式的返回值类型:

// 返回never的函数必须存在无法达到的终点
function error(message: string): never {
  throw new Error(message);
}

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

never类型也可以做类型注解:

let value: never // ok

还有个关键应用场景,


function foo(x: string | number): boolean {
  if (typeof x === 'string') {
    return true;
  } else if (typeof x === 'number') {
    return false;
  }

  // 如果fail不是一个 never 类型,这会报错:
  // - 不是所有条件都有返回值 (严格模式下)
  // - 或者检查到无法访问的代码
  // 但是由于 TypeScript 理解 `fail` 函数返回为 `never` 类型
  // 它可以让你调用它,因为你可能会在运行时用它来做安全或者详细的检查。
  return fail('Unexhaustive');
}

function fail(message: string): never {
  throw new Error(message);
}

never类型和void类型的区别:

一旦有人告诉你,never 表示一个从来不会优雅的返回的函数时,你可能马上就会想到与此类似的 void,然而实际上,void 表示没有任何类型,never 表示永远不存在的值的类型。

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

2.10 Tuple (元组)类型

元组类型是TypeScript中特有的类型,我们知道,数组中一般存放的是类型相同的元素,如果想存储不同类型的元素,就用到了元组类型, 元组可用于定义具有有限数量的未命名属性的类型:

let value: [string, number, boolean] = ['hsh', 18, true]   // ok
let value1: [string, number, boolean] = ['hsh', '18', true] // Error
let value2: [string, number, boolean] = ['hsh', 18, 'true']  // Error

使用元组时,必须提供每个属性的值:

let value: [string, number] = ['hsh'] 
// Error
// Type '[string]' is not assignable to type '[string, number]'.
// Source has 1 element(s) but target requires 2.

可以通过索引来取元组中的元素:

let value: [string, number] = ['hsh', 123]
value[0] // 'hsh'  类型:string
value[1] // 123  类型: number 

2.11 函数的参数类型和返回值类型

函数的参数添加类型注解:

// 函数的参数是基本类型
function sum(num1: number, num2: number): number {
  return num1 + num2 // 返回值类型是`number`,类型自动推导出来
}

// 函数的参数是对象类型
function printPoint(point: {x: number, y: number}) {
  console.log(point.x)
  console.log(point.y)
}

这里提一下可选类型 ,语法是?:,看下边的例子:

// `z`就是可选类型
// 函数调用的时候,可传`z`,也可不传
function printPoint(point: {x: number, y: number, z?: number}) {
  console.log(point.x)
  console.log(point.y) 
  console.log(point.z) // 不传`z`的时候,值为`undefined`
}

printPoint({
    x: 100,
    y: 200
}) // ok 

printPoint({
    x: 100,
    y: 200,
    z: 300
}) // ok

2.12 联合类型

联合类型是由两个或者多个其他类型组成的类型,其主要的目的就是缩小类型的范围,有助于开发人员写出更健壮的代码。

function printId(id: string | number | boolean) {
  // 使用联合类型时  需要先判断类型
  if(typeof id === 'string') {
    id.toLocaleUpperCase();
  } else {
    console.log(id)
  }
}
printId('111') // ok
printId(111)  // ok
printId(false) // ok

2.13 类型别名

类型别名用来给一个类型起个新名字,这样可以起到类型复用的目的,同时也减少了代码量。

// 基本类型的类型别名
type IdType = string | number | boolean

function printIdNew(id: IdType) {
  console.log(id)
}

printIdNew('1') // ok
printIdNew(1)  // ok
printIdNew(true)  // ok 

// 对象类型的类型别名
type PointType = {
  x: number,
  y: number
}
function printPointNew(point: PointType) {
  console.log(point.x) 
  console.log(point.y)
}
printPointNew({x: 1, y: 2}) // ok

3. 总结

这篇文章主要介绍了TypeScript的一些类型和类型操作,其实非常建议大家去手动的敲一遍文章中的例子,如果知识看一遍的话,很快就会忘掉的。

这篇文章是从0开始学习TypeScript的第二篇,[第一篇传送门](邂逅TypeScript,搭建简易ts开发环境 - 掘金) 如果你也准备开始学习,可以关注我,第三篇会及时更新。

持续更新中...