添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《 阿里云开发者社区用户服务协议 》和 《 阿里云开发者社区知识产权保护指引 》。如果您发现本社区中有涉嫌抄袭的内容,填写 侵权投诉表单 进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

类型与类型推断

当我们初学ts的时候,我们可能会写出这样的程序

let price: number = 123_456_789;//当数字太长,你可以使用下划线来分割以获取更好的阅读性
let item: string = "TypeSript";
let isNeed: boolean = true;

但实际上,ts编译器可以推断或者检测变量的类型

例如,我们已经将 price 初始化为一个数字,那么ts编译器将能够检测到这是一个numebr类型,这时候我们声明类型就是多余的了。

当我们声明了却没有初始化变量时,ts会将其推断为any类型(any类型表示 所有类型 ,是所有类型的父类型,在ts中请尽量少的使用它)

这个时候我们可以给 title 赋不同类型的值,这并不是我们想看到的, 所以我们应该在声明没有初始化的变量时,定义变量的类型

在我们声明函数时,我们可能不知道属性的类型具体是什么,这时候ts会给我们这个提示

这时候我们有两种选择:

1.将属性的类型显式声明为any。

2. ctrl+P 搜索tsconfig.json文件,并找到 "noImplicitAny": true, 将其去掉注释并改为 false (不建议,除非有大量的错误,但那样你可能会失去使用ts的意义)

Array

同样的,如果我们声明一个空数组

const arr = [];//它的类型为any[]

由前面的经验,我们明白,我们应该给arr声明类型

const arr: number[] = [];//类型为number[]

ts有一个特性:在使用某个确定类型的变量时,你能够得到相应类型方法的代码补全提示。

这也是我们为什么使用ts的一个重要原因

Tuples/元组

ts有一个名为Tuples/元组的新类型,这是一个拥有固定长度的数组,并且每个元素都有一个特定的类型。我们经常会在处理一对值时使用它。

let user: [number, string] = [20, "kevin"];//类型为[number, string] 注意,这里的类型与元素的位置是对应的
//let user: [number, string] = ["kevin", 20]; //错误:不能将类型“string”分配给类型“number”。

同时,当我们声明第三个元素时,将会报错,因为元组的元素长度是固定的。

let user: [number, string] = [20, "kevin", 0];
//错误:不能将类型“[number, string, number]”分配给类型“[number, string]”。源具有 3 个元素,但目标仅允许 2 个。ts(2322)

关于元组,你应该知道的是:在内部,使用的是普通的js数组,如果我们编译代码,那么得到的只是一个普通的js数组:

"use strict";
let user = [20, "kevin"];
//# sourceMappingURL=demo.js.map

关于元组,有一个需要注意的是,虽然元组是固定长度的数组,但你仍然可以使用push添加元素。这也许是一个漏洞,希望将来会得到解决。

let user: [number, string] = [20, "kevin"];
user.push("demo");//只要类型为 number|string 并不会报错

关于元组的最佳实践:当数组只有两个值的时候可以使用(如键值对),但当你有更多的元素,使用元组可能让你的代码的阅读性更差。

Enum/枚举

枚举与数组类似,在以前我们定义三个常量可能会这样做:

const small = 1;
const medium = 2;
const large = 3;

不过在ts中,我们可以使用枚举:

enum SizeList {
  Small,//默认为0
  Medium,//后面的值依次增长
  Large,

当然,你可以显式的设置值

enum SizeList {
  Small = 1,//定义为1
  Medium,//值为2,以此类推
  Large,

或者将值声明为其他类型,不过这时候,你需要声明每个常量的值:

enum SizeList {
  Small = "s",
  Medium = "m",
  Large = "l",

使用枚举:

enum SizeList {
  Small = 1,
  Medium,
  Large,
let mySize: SizeList = SizeList.Medium;
console.log(mySize);//2

我们看到编译后的js文件,代码似乎有些长:

"use strict";
var SizeList;
(function (SizeList) {
    SizeList[SizeList["Small"] = 1] = "Small";
    SizeList[SizeList["Medium"] = 2] = "Medium";
    SizeList[SizeList["Large"] = 3] = "Large";
})(SizeList || (SizeList = {}));
let mySize = SizeList.Medium;
console.log(mySize);
//# sourceMappingURL=demo.js.map

我们可以通过一个小技巧,让ts编译器生成更加优化的代码:在enum前添加const

const enum SizeList {
  Small = 1,
  Medium,
  Large,
let mySize: SizeList = SizeList.Medium;
console.log(mySize);

下面是编译后的js文件:

"use strict";
let mySize = 2;
console.log(mySize);
//# sourceMappingURL=demo.js.map

总结:我们可以通过枚举表示相关常量的列表,如果我们const声明enum,编译器将生成更加优化的代码。

function

在声明函数时,根据最佳实践,我们应该对函数的属性和返回值的类型进行声明:

function taxation(income: number) {}//它的类型为function taxation(income: number): void
//我们应该这样做:
function taxation(income: number): number {
  return 0;
}//它的类型为function taxation(income: number): number

对于上面的函数,我们并没有在函数中使用到传入的参数,我们希望ts能够给我们警告,我们可以在tsconfig.json中配置它:

找到"noUnusedParameters": true,并打开它

这时候我们就得到一个警告了

我们写这个函数:

function taxation(income: number): number {
  if (income < 50_000) return income * 1.2;
}//函数缺少结束 return 语句,返回类型不包括 "undefined"。

出现这个报错的原因是,只有在符合条件时才会返回income*1.2,而不符合则返回undefined(在js中,未赋值的变量都为undefined),而undefined不为number类型。

我们可以去除函数的返回值类型

function taxation(income: number) {
  if (income < 50_000) return income * 1.2;
}//function taxation(income: number): number | undefined

这样不会报错,但这样并不符合ts的使用标准,我们不希望会返回undefined,我们希望我们这样做的时候ts对我们进行警告

我们可以在tsconfig.json中找到"noImplicitReturns": true,并打开它。

这样,你就得到想要的警告了。

对于上面的函数,我们应该这样写:

function taxation(income: number): number {
  if (income < 50_000) return income * 1.2;
  return income * 1.3;

在函数中,我们可能声明了变量/常量却没有使用,我们同样希望得到提示:

function taxation(income: number): number {
  let a = 1;
  if (income < 50_000) return income * 1.2;
  return income * 1.3;

我们可以在tsconfig.json中找到"noUnusedLocals": true,并打开它。

这时候我们将得到一个提示:已声明“a”,但从未读取其值。ts(6133)

现在我们的函数有两个参数,但我们在调用的时候传入了三个,这时候ts会给我们报错,而在js中并不会报错

function taxation(income: number, taxYear: number): number {
  if (taxYear < 2022) return income * 1.2;
  return income * 1.3;
taxation(300_000, 2023, 1);//报错:应有 2 个参数,但获得 3 个。ts(2554)

我们可能会想让taxYear属性为可选属性,我们可以在冒号前使用来声明可选属性。

但通常情况下,ts会给我们报错:

function taxation(income: number, taxYear?: number): number {
  if (taxYear < 2022) return income * 1.2;
  return income * 1.3;
}//对象可能为“未定义”。ts(2532)

因为如果我们没有传入taxYear,那么taxYear在函数中就为undefined,但undefined转换为数字后为NAN。并不能进行比较

对此,我们有两个解决方法:

1.旧版写法(不推荐):

function taxation(income: number, taxYear?: number): number {
  if ((taxYear || 2022) < 2022) return income * 1.2;
  return income * 1.3;

2.es6写法(推荐):

function taxation(income: number, taxYear = 2023): number {
  if (taxYear < 2022) return income * 1.2;
  return income * 1.3;
taxation(300_000, 2024);//taxYear的默认值为2023,调用时传入2024,将会覆盖掉默认值

总结:作为最佳实践,我们应该始终正确的注释函数,参数类型,返回值类型。并且启用tsconfig.json中的

"noUnusedParameters": true,(没有未使用的参数),"noUnusedLocals": true,(没有未使用的变量),"noImplicitReturns": true,(没有隐式返回)

Object

当我们像js那样使用对象时,ts会给我们报错

let man = { age: 22 };
man.name = "kevin";//报错:类型“{ age: number; }”上不存在属性“name”。ts(2339)

在ts中使用对象时,我们也应该像function那样,明确的声明对象的类型:

let man:{
    name:string,
    age:number
} =  { age: 22 };//类型 "{ age: number; }" 中缺少属性 "name",但类型 "{ name: string; age: number; }" 中需要该属性。ts(2741)
man.name = "kevin";

这时候同样会报错,因为我们在声明对象时没有对对象的属性进行声明。也就是没有声明name属性。

我们可以使用ts的特性,让其属性为可选属性

let man: {
  name?: string;
  age: number;
} = { age: 22 };
man.name = "kevin";

但是,我们不应该这样使用。

我们不能盲目地使用ts的特性。我们在写代码的同时,也应该考虑代码的合理性,毕竟没有一个man的name为undefined

所以,我们还是应该这样使用:

let man: {
  name: string;
  age: number;
} = { age: 22, name: "kevin" };

在声明对象时,我们可能希望某些属性不被更改。对此,我们可以使用readonly关键字,它表示(只读的)

let man: {
  readonly name: string;
  age: number;
} = { age: 22, name: "kevin" };
man.name = "qian";//无法分配到 "name" ,因为它是只读属性。ts(2540)

对象的方法

在声明方法时,我们同样的要给函数声明函数签名(也叫类型签名,或方法签名,定义了函数或方法的输入与输出)

let man: {
  readonly name: string;
  age: number;
  retire: (date: Date) => void;//函数签名
} = {
  age: 22,
  name: "kevin",
  retire: (date: Date) => {
    console.log(date);