TS入门总结

基本数据类型

我们都知道js有6种基本数据类型:布尔、数字、字符串、null、undefined和es6新增的Symbol + 1种引用数据类型:对象(包含Object、Function、Array、Date等等)类型。

ts作为js的超集,ts也有一套数据类型和上述7中数据类型进行一一对应。这里我们先介绍5中基本类型:布尔、数字、字符串、null和undefined,Symbol暂时不会提到。

布尔值

基本数据类型,它有两个值:true/false。在ts中是这样表示一个bool值的:

const isDone: boolean = false;
值得注意的是,不能将构造函数Boolean实例化的值赋给boolean:

const isDone: boolean = new Boolean(false);

/*
不能将类型“Boolean”分配给类型“boolean”。
“boolean”是基元,但“Boolean”是包装器对象。如可能首选使用“boolean”。
*/
实际上,new Boolean()返回的类型是Boolean,并非boolean:

// 正常编译通过(后面没有标明错误的都表示正常编译通过)
const isDone: Boolean = new Boolean(false);
我们需要区分清楚,boolean是js的基本数据类型,而Boolean是js中的构造函数,返回的对象是以Boolean为原型的js对象,使用ts的时候要注意这个区别。包括后面的其他数据类型也是一样的问题,后面不再赘述。

数字

基本数据类型,js和ts中的数字全部都是浮点数。在ts中表示一个数值:

let decLiteral: number = 6;
// 十六进制
let hexLiteral: number = 0xf00d;
// 二进制
let binaryLiteral: number = 0b1010;
// 八进制
let octalLiteral: number = 0o744;
字符串

基本数据类型,js和ts都可以使用 " 符号或者 ’ 符号包裹来表示字符串,也可以使用模板字符串 ` 符号进行包裹:

let str: string = ‘前端充电’;
let name: string = “aaa”;

// 使用模板字符串
let tempStr: string = 欢迎关注${name}-${str};
Null和undefined

这两个基本数据类型,同样可以在ts中分别使用null和undefined来表示:

let u: undefined = undefined;
let n: null = null;
默认情况下,null和undefined是其他类型的子类型。什么意思呢?也就是说,你可以将null和undefined赋值给其他类型的数据而不会编译报错。例如:

let age: number = undefined;
let people: string = null;
如果你在自己的项目中尝试的时候发现报错了,可以试试查看下项目文件中有没有一个tsconfig.json的文件:

这是ts的配置文件,里面可以定制化自己的ts规则。比如上面所讲的可以将null和undefined赋值给其他类型也是可以定制化的。修改配置文件里面的strictNullChecks属性为true,就会发现上述两行代码就会报错了:

let age: number = undefined;
let people: string = null;

/*
不能将类型“undefined”分配给类型“number”。
不能将类型“null”分配给类型“string”。
*/
strictNullChecks就表示是否只允许null和undefined赋值给void类型和它们各自的类型,ts官方建议这个配置默认为true,可以避免问题查找过于混乱。

void

上面提到了void类型,这个是ts独有的类型。可以理解为空值,表示没有任何类型。声明为void的变量只能赋值为null或者undefined:

let age: void = undefined;
这里插播一点,null可以赋值给void是ts官方的定义。但是我亲自测试的时候发现无论是变量的null还是函数返回值null赋值给void都会报错。所以关于null是否能赋值给void这一点还有待确定,当然如果你知道原因的话也可以留言给我解答一下,这里我不能随意带过所以就提出来了。

let name: void = null;
let result = (): void => null;

/*
不能将类型“null”分配给类型“void”。
*/
好,我们接着往下看。void除了可以赋值为undefined,还可以表示没有返回值的函数:

// (): void的写法表示函数的返回值类型,详细的会在后面函数部分讲到。
let getData = (): void => console.log(‘这里没有任何返回值。’);
要注意的是,没有返回值的函数并非是指返回undefined,实际上它的返回值类型就是void。所以要注意不要定义函数返回类型为undefined,下面的写法会报错:

let getData = (): undefined => console.log(‘这里没有任何返回值。’)

/*
不能将类型“void”分配给类型“undefined”。
*/
any

ts独有类型,有时候我们还不能确定一个变量到底会被赋予什么类型的值的时候,就可以定义为any类型。any表示可以是任意的类型:

let a: any = 1;
let b: any = true;
let c: any = null;
any可以在编译过程中被赋予任意类型的值:

let a: any = 1;
a = undefined;
a = ‘字符串’;
可以任意访问any类型的内部属性:

let a: any;
console.log(a.anyProp.anyOtherFn());
在声明变量的时候未指定类型也会被识别为any类型:

let a;
a = ‘任意类型’;
a = 2;
一定要尽量避免使用any!!!否则ts就和js没什么差别了!

never

ts独有的类型,表示永远不会存在的值的类型。never类型的值可以赋值给任意类型,但是除了never类型,没有其他类型可以赋值给never类型,即使是any类型也不行;

let neverType: never;
let anyType: any;
neverType = anyType;

/*
不能将类型“any”分配给类型“never”。
*/
never类型一般使用在那些总是会抛出异常的函数中:

function throwErr(): never {
throw new Error(‘something throng’);
}
又或者是无法结束循环的函数中:

function infiniteLoop(): never {
while(true) {}
}
引用数据类型

上面我们提到js有1种引用数据类型:对象。实际上对象是一种统称,因为在js中万物皆对象。对象包含了js对象、数组、函数等等引用类型,我们逐步看看ts又是如何表示这些类型的。(后面提到的对象指代js通过new Object创建出来的对象)

对象

引用数据类型,这里ts就不能简单地通过某一种类型来对其进行描述啦。因为对象的可能性是无限的,不能通过某种类型来单一定义。

ts中使用接口(interface)来定义对象。所谓接口其实跟传统意义上的面向对象的接口有些差别,传统意义上的接口更多是对行为的抽象化,而ts的接口是描述一个对象的形状(shape)。

我们直接来看下面的例子:

interface IAnimal {
name: string;
eat (): void;
}
上面定义了Animal接口,ts中规定interface的第一个字母必须是大写的"i"。IAnimal有一个name属性是string类型,有一个返回值是void的eat方法。下面我们根据这样的接口定义变量:

let cat: IAnimal = {
name: ‘Tom’,
eat () {
console.log(‘喝牛奶’);
}
}
上面我们定义了cat变量为IAnimal类型,并且给name变量赋了值还有实现了eat方法,这就是接口的最简单的使用。

只要有缺失的属性,ts编译就会报错:

interface IAnimal {
name: string;
eat (): void;
}
let cat: IAnimal = {
name: ‘Tom’
}

/*
类型 “{ name: string; }” 中缺少属性 “eat”,但类型 “IAnimal” 中需要该属性。
*/
同样的,有多余的属性编译也是不通过的:

interface IAnimal {
name: string;
eat (): void;
}
let cat: IAnimal = {
name: ‘Tom’,
eat () {
console.log(‘喝牛奶’);
},
age: 18
}

/*
不能将类型“{ name: string; eat(): void; age: number; }”分配给类型“IAnimal”。
对象文字可以只指定已知属性,并且“age”不在类型“IAnimal”中。
*/
接口可以定义可选属性,表示该属性可以存在也可以不存在。可选属性定义如下:

interface IAnimal {
name: string;
eat (): void;
age?: number;
}
这时候无论实现的对象是否包含age属性都不会报错:

let cat: IAnimal = {
name: ‘Tom’,
eat () {
console.log(‘喝牛奶’);
},
age: 18
}
接口可以定义任意属性,任意属性允许我们传入一个任意的变量,一个接口只允许定义一个任意属性:

interface IAnimal {
name: string;
eat (): void;
age?: number;
propName: string: any;
}

let cat: IAnimal = {
name: ‘Tom’,
eat () {
console.log(‘喝牛奶’);
},
age: 18,
anyProp: ‘这是任意变量’,
anyProp2: true
}
任意属性涉及到的点有点多。。。我们一一来理一下。

首先是IAnimal中的propName: string: any这句话:

接口如果定义了这句话,你就可以在具体的对象中放置任意数目的属性而不用担心编译错误。propName: string表示的是最终只会取键类型是string的值,所以如果键的类型是number、symbol等等最后是不会存在cat中的。

上面的any表示的是允许的值类型,要注意一旦定义了任意属性,那么确定属性和可选属性都必须是任意类型的子集。例如下面的任意属性就会报错:

interface IAnimal {
name: string;
eat (): void;
age?: number;
propName: string: string;
}

/*
类型“() => void”的属性“eat”不能赋给字符串索引类型“string”。
类型“number | undefined”的属性“age”不能赋给字符串索引类型“string”。
*/
因为任意属性定义的类型是string,所以其他属性必须为string。所以这也是为什么要在特定场合才使用任意属性的原因,限制太多。

接口可以定义只读属性,只需要在前面添加一个readonly即可:

interface IAnimal {
readonly name: string;
eat (): void;
age?: number;
}
当我们打算为只读属性赋值就会报错:

let cat: IAnimal = {
name: ‘Tom’,
eat () {
console.log(‘喝牛奶’);
},
age: 18,
}
cat.name = ‘Jerry’;

/*
无法分配到 “name” ,因为它是只读属性。
*/
数组

下面来看看第二种常见的类型:数组。数组可以使用3种方式来定义,但是有一些不是ts官方建议使用的,这里我们都来看一下。

第一种:类型 + 方括号表示法(推荐)

const arr: number[] = [1, 2, 3];
这是最简单的数组表示方法,number表示数组每一项的类型必须为number。

第二种:泛型

关于泛型的详细讲解我放在了下一篇ts进阶文章,这里大概看一下其写法就行了:

const arr: Array = [1, 2, 3];
Array后面接尖括号,里面的number参数就表示数组的每一项必须是number,约束和第一种写法是一样的。

第三种:接口

还记得我们刚刚讲过的任意属性吗?可以利用它来表示数组:

interface IArr {
[i: number]: number
}

let arr: IArr = [ 1, 2, 3 ];
这种用法真的很巧妙!不过我们一般不推荐这种用法,比较复杂而且可读性差。

如果我们想要一个存放任意类型数据的数组,可以定义any类型的数组:

let arr: any[] = [ 1, true, 2, ‘这是任意类型数组’ ];
函数

接下来我们看一下函数类型,我们前面也已经有写过部分函数类型了:

let getData = (): void => console.log(‘这里没有任何返回值。’);
这是函数表达式搭配es6箭头函数的写法(ts原生语法就支持es6),函数不接受参数,返回值是void类型,也就是不返回任何值。

函数可以接收参数,参数的类型需要指定:

function add(num1: number, num2: number): number {
return num1 + num2;
}
函数还可以通过函数表达式的方式定义:

let sum = (num1: number, num2: number): number => num1 + num2;
但是通过函数表达式定义的函数有个问题,就是左侧的sum变量还没有为它赋予类型。要为sum赋予类型的话,必须赋值为完整的函数类型,包含参数和返回值:

let sum: (num1: number, num2: number) => number
= (num1: number, num2: number): number => num1 + num2;
注意上面的 => number和下面的箭头函数=> num1 + num2的差别,第一个是ts语法中的表示函数执行后返回值类型,第二个则是箭头函数特定写法。

函数可以定义可选参数和默认参数

和接口类似,函数的参数类型可以为可选参数,可以为参数添加默认参数。我们看个例子:

function add(num1: number, num2: number, isDouble?: boolean): number {
return isDouble ? (num1 + num2) * 2 : (num1 + num2);
}

add(1, 2); // 3
add(2, 5, true); // 14
上面函数中定义了一个可选参数:isDouble,调用者可以选择是否传递该参数,如果不传的话就是undefined。要注意可选参数不允许出现在必选参数前面:

function add(isDouble?: boolean, num1: number, num2: number): number {
return isDouble ? (num1 + num2) * 2 : (num1 + num2);
}

/*
必选参数不能位于可选参数后。
*/
原因也很容易理解,可选参数在前面的话调用者就没办法传参了。下面我们来看下默认参数写法:

function add(num1: number, num2: number, isDouble?: boolean, coefficient: number = 2): number {
return isDouble ? (num1 + num2) * coefficient : (num1 + num2);
}

add(1, 2); // 3
add(4, 6, true); // 20
add(4, 6, true, 3); // 30
这里增加多了一个默认参数:coefficient,和js函数的默认参数定义是一样的,如果没有传递则用默认值。

函数可以通过…rest来接收剩余参数

你可以通过一个变量来收集所有的参数:

function eat(food: string, …rest: string[]): void {
console.log(‘eat:’, food, rest);
}

eat(‘香蕉’, ‘菠萝’, ‘苹果’); // eat: 香蕉 [“菠萝”, “苹果”]
这个变量实际上就是一个数组,要注意它同样得放到参数最后面(在可选参数之后)。

类型推论

ts可以根据上下文语境动态推断类型。

在没有指定类型的时候推断:
let str = ‘字符串1’;

// 等价于

let str: string = ‘字符串1’;
在定义时没有赋值:
let str;

//等价于

let str: any;
联合接口

联合类型用于表示变量可能取值为多个变量之一,如下:

let a: string | number;
变量a的可能取值是string或者number,在赋值之前它的类型都是不确定的状态。但是如果给它赋予其它类型的值就会报错:

a = true;

/*
不能将类型“true”分配给类型“string | number”。
*/
当联合类型不能确定是哪种类型时,我们只能访问两种类型都存在的属性或者方法,否则就会编译错误。

let a: string | number;
console.log(a.length);

/*
类型“string | number”上不存在属性“length”。
类型“number”上不存在属性“length”。
*/
当为变量赋值之后,其类型也就确定下来了,无法再使用另一个类型的属性。

let a: string | number = 12;
console.log(a.length);

/*
类型“number”上不存在属性“length”。
*/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值