前言
最近工作一直很忙,复盘周期也有所拉长,不过还是会坚持每周复盘。今天笔者将复盘一下typescript在前端项目中的应用,至于为什么要学习typescript,我想大家也不言自明,目前主流框架vue和react以及相关生态的内部构建大部分都采用了typescript,其原因就在于它的静态类型检查极大的提高了代码的可读性和可维护性,而且定位问题非常方便。下面上一份关于typescript的官方定义,方便大家理解:
TypeScript 是由微软开发的自由和开源的编程语言, 是JavaScript 的一个超集,支持 ECMAScript 6 标准。其设计目标是开发大型应用,它可以编译成纯 JavaScript,编译出来的 JavaScript 可以运行在任何浏览器上。
任何语言的学习都要有学习和思考体系,前端也不例外,笔者将按照如下图所示结构来进行讲解:
我们目前项目开发用的最多的就是webpack,对于ts,我们也很方便的可以通过ts-loader对其进行编译配置,为了降低大家学习ts的难度,笔者推荐采用vue-cli3或者umi直接搭建ts项目,这样可以更快的上手ts开发。
1.基础类型TypeScript支持与JavaScript几乎相同的数据类型,此外还提供了实用的枚举类型方便我们使用。
接下来我们简单介绍一下这几种类型的用法.
// 布尔类型
let isCookie:boolean = true
// 数值类型
let myMoney:number = 12
// 字符串类型
let name:string = '徐小夕'
// 数组类型, 有两种表示方式,第一种可以在元素类型后面接上[],表示由此类型元素组成的一个数组
let arr:number[] = [1,2,2]
// 数组类型, 使用数组泛型
let arr:Array<number> = [1,2,2]
// 元组类型, 允许表示一个已知元素数量和类型的数组,各元素的类型不必相同
let xi: [string, number];
// 初始化xi
xi = ['xu', 10]; // 正确
xi = [11, 'xu']; // 错误
// 枚举类型, 可以为一组数值赋予友好的名字
enum ActionType { doing, done, fail }
let action:ActionType = ActionType.done // 1
// any, 表示任意类型, 可以绕过类型检查器对这些值进行检查
let color:any = 1
color = 'red'
// void类型, 当一个函数没有返回值时,通常会设置其返回值类型是 void
function getName(): void {
console.log("This is my name");
}
// object类型, 表示非原始类型,也就是除number,string,boolean,symbol,null或undefined之外的类型
let a:object;
a = {props: 1}
以上是typescript中常用的几种类型, 也是我们必须掌握的基本知识. 这里值得补充的是typescript的类型断言, 也是解决ts警告的利器,比如我们确切的知道某种数据的数据类型,我们可以这么做:
let arrLen: number = (someValue as Array<string>).length;
// 解决window下设置属性的ts报错, 但不可滥用
(window as any).name = 'xuxi'
2.接口
TypeScript的核心原则之一是对值所具有的结构进行类型检查。 在TypeScript里,接口的作用就是为这些类型命名和为你的代码或第三方代码定义契约。接下来我们看看如何定义和使用接口(Interface):
interface Product {
name: string;
size: number;
weight: number;
}
let product1:Product = {
name: 'machine1',
size: 20,
weight: 10.5
}
类型检查器不会去检查属性的顺序,只要相应的属性存在并且类型也是对的就可以。其次我们还可以定义可选属性和只读属性. 可选属性表示了接口里的某些属性不是必需的,所以可以定义也可以不定义.可读属性使得接口中的某些属性只能读取而不能赋值. 具体案例如下:
interface Product {
name: string;
size?: number;
readonly weight: number;
}
在实际场景中, 我们往往还会遇到不确定属性名和属性值类型的情况, 这种情况往往发生在第三发SDK接入或者后端响应中, 这个时候我们可以利用索引签名来设置额外的属性和类型, 案例如下:
interface App {
name: string;
color?: number;
[propName: string]: any;
}
接口除了描述带有属性的普通对象外,也可以描述函数类型。我们需要给接口定义一个调用签名, 参数列表里的每个参数都需要名字和类型。案例如下:
interface MyFunc {
(value:string, type: number): boolean;
}
// 使用
let myLoveFront: MyFunc;
myLoveFront = function(value:string, type: number) {
return type > 1
}
我们在vue和react开发中,也会经常使用class这种类来编写可复用组件和库, 既然ts可以描述函数类型, 那么是不是也可以用来描述类类型呢? 答案是可以的.但是类接口的定义稍微有点复杂, 我们都知道类是具有两个类型的:静态部分的类型和实例的类型. 当一个类实现了一个接口时,只对其实例部分进行类型检查。constructor存在于类的静态部分,所以不在检查的范围内。. 这句话相当关键, 我们在定义类接口的时候也要主要这一特点, 案例如下:
interface TickConstructor {
new (hour: number, minute: number): TickInterface;
}
interface TickInterface {
tick();
}
function createClock(ctor: ClockConstructor, hour: number, minute: number): TickInterface {
return new ctor(hour, minute);
}
class DigitalClock implements TickInterface {
constructor(h: number, m: number) { }
tick() {
console.log("xu xu");
}
}
class MyTick implements TickInterface {
constructor(h: number, m: number) { }
tick() {
console.log("tick tock");
}
}
let digital = createClock(DigitalTick, 12, 17);
let analog = createClock(MyTick, 7, 32);
掌握了这些关键的接口类型和使用方法, 对于ts的学习基本上可以入门了.
3.类
关于类接口的话题我们在上文已经介绍了, 这里我们来具体了解一下类. 和js的class一致, typescript的类有公共,私有与受保护的修饰符. 具体含义如下:
具体案例如下:
class Person {
protected name: string;
constructor(name: string) { this.name = name; }
}
class Employee extends Person {
private department: string;
constructor(name: string, department: string) {
super(name)
this.department = department;
}
public getElevatorPitch() {
return `Hello, my name is ${this.name} and I work in ${this.department}.`;
}
}
同样我们也可以为类中的某个属性定义readonly修饰符和定义static静态属性, 唯一值得说的是抽象类.
抽象类做为其它派生类的基类使用。它们一般不会直接被实例化。不同于接口,抽象类可以包含成员的实现细节。abstract关键字是用于定义抽象类和在抽象类内部定义抽象方法。
有关抽象类的案例简单介绍如下:
abstract class MyAbstract {
constructor(public name: string) {}
say(): void {
console.log('say name: ' + this.name);
}
abstract sayBye(): void; // 必须在派生类中实现
}
class AccountingMyAbstract extends MyAbstract {
constructor() {
super('小徐'); // 在派生类的构造函数中必须调用 super()
}
sayBye(): void {
console.log('趣谈小夕.');
}
getOther(): void {
console.log('loading...');
}
}
let department: MyAbstract; // 允许创建一个对抽象类型的引用
department = new MyAbstract(); // 错误: 不能创建一个抽象类的实例
department = new AccountingMyAbstract(); // 允许对一个抽象子类进行实例化和赋值
department.say();
department.sayBye();
department.getOther(); // 错误: 方法在声明的抽象类中不存在
4.函数
函数类型在上文已经介绍过了, 这里主要在讲一下可选参数这个概念. JavaScript里每个参数都是可选的,可传可不传。没传参的时候其值就是undefined。在TypeScript里我们可以在参数名旁使用 ?实现可选参数的功能。具体案例如下:
function createName(firstName: string, lastName?: string) {
if (lastName)
return firstName + " " + lastName;
else
return firstName;
}
注意, 我们的可选参数必须跟在必选参数后面.
5.泛型
我们可以使用泛型来创建可重用的组件,一个组件可以支持多种类型的数据。这样用户就可以以自己的数据类型来使用组件。泛型是typescript中比较难懂的知识点, 但是非常重要, 几乎任何第三方组件库里都会用到. 我们先来看个最简单的例子:
function iSay<T>(arg: T): T {
return arg;
}
// 调用泛型函数
let come = iSay<number>(123);
我们给iSay添加了类型变量T。T帮助我们捕获用户传入的类型(比如:string),这样我们就可以使用这个类型。之后我们再次使用T当做返回值类型。现在我们可以知道参数类型与返回值类型是相同的了。这允许我们跟踪函数里使用的类型的信息。
我们还可以把泛型变量T当做类型的一部分使用,而不是整个类型, 这样可以增加我们的使用灵活性, 案例如下:
function iSay<T>(arg: T[]): T[] {
console.log(arg.length)
return arg;
}
类似于函数类型的定义, 我们也可以定义泛型接口, 并且可以把泛型参数当作整个接口的一个参数, 这样我们就能清楚的知道使用的具体是哪个泛型类型. 案例代码如下:
interface SayLove {
<T>(arg: T): T
}
// 把泛型参数当作整个接口的一个参数
interface SayLoveArg<T> {
(arg: T): T
}
// 泛型函数
function iSay<T>(arg: T): T {
return arg;
let mySay1:SayLove = iSay
let mySay2:SayLoveArg<number> = iSay
同样的我们还可以定义泛型类.我们只需要使用(<>)括起泛型类型,跟在类名后面即可. 具体案例如下:
class MyNumber<T> {
year: T;
compute: (x: T, y: T) => T;
}
let myGenericNumber = new MyNumber<number>();
我们还可以定义泛型约束来更准确的控制类的类型. 案例如下:
interface NumberControl {
length: number
}
class MyObject<T extends NumberControl>(arg: T):T {
console.log(arg.length)
return arg
}
6.高级类型typescript的高级类型里我们主要讲解如下核心知识点:
交叉类型是将多个类型合并为一个类型。我们可以把现有的多种类型叠加到一起成为一种类型,下面有个经典的例子供大家参考:
function extend<T, U>(first: T, second: U): T & U {
let result = <T & U>{};
for (let id in first) {
(<any>result)[id] = (<any>first)[id];
}
for (let id in second) {
if (!result.hasOwnProperty(id)) {
(<any>result)[id] = (<any>second)[id];
}
}
return result;
}
我们通过字符 & 来表示联合, 此时以上代码中的返回值会具有T和U的类型.
联合类型表示一个值可以是几种类型之一。我们用竖线(|)分隔每个类型,所以 number | string | boolean表示一个值可以是 number, string,或 boolean。具体例子如下:
let name: string | number = 'xuxiaoxi'
function sayName(name: string):(string|number) {
return name
}
值得注意的是: 如果一个值是联合类型,我们只能访问此联合类型的所有类型里共有的成员。
还有一种常见的需求是, 我们在实现自己的类后,需要支持类方法的链式调用, 这个时候我们应该返回this, 在typescript中我们就需要了解多态的 this类型. 它表示的是某个包含类或接口的子类型。这被称做 F-bounded多态性。要想在typescript中支持链式, 我们可以这么写:
class MyCalculator {
public constructor(number = 0) { }
public add(n: number): this {
this.value += n;
return this;
}
public multiply(n: number): this {
this.value *= n;
return this;
}
// ... 其他操作 ...
}
let v = new MyCalculator(2).multiply(5).add(1);
下面一个我们需要知道的知识点是索引类型查询操作符. 一般用keyof表示。对于任何类型T, keyof T的结果为T上已知的公共属性名的联合。比如我们定义一个接口Animal:
interface Animal {
cat: string;
dog: string;
}
let AnimalProps: keyof Animal; // 'cat' | 'dog'
keyof Animal是完全可以与 'cat' | 'dog'互相替换的。不同的是如果我们添加了其它的属性到 Animal,例如 pig: string,那么 keyof Animal会自动变为 'cat' | 'dog' | 'pig'。
7.命名空间命名空间主要作用是用来组织代码,以便于在记录它们类型的同时还不用担心与其它对象产生命名冲突。由于命名空间的用法很简单,这里我们以网上比较流行的D3作为例子, 代码如下:
declare namespace D3 {
export interface Selectors {
select: {
(selector: string): Selection;
(element: EventTarget): Selection;
};
}
export interface Event {
x: number;
y: number;
}
export interface Base extends Selectors {
event: Event;
}
}
declare var d3: D3.Base;
8.使用第三方类库
在熟悉以上基础知识之后, 我们看一下如何使用支持typescript的第三方类库. 比如说我们常用的lodash, 那么正确的使用步骤如下:
// 安装lodash和对应的类型文件
npm install --save lodash @types/lodash
// 在代码中使用
import * as _ from "lodash";
_.padStart("Hello xuxiaoxi!", 12, " ");
9.声明文件
声明文件也是一个非常重要的知识点.对于使用未经声明的全局函数或者全局变量, typescript往往会报错, 所以我们可以在对应位置添加xxx.d.ts文件, 并在里面声明我们所需要的变量, ts会自动检索到该文件并进行解析. 以下是几个案例, 供大家参考学习:
// global.d.ts
// 声明全局变量
declare var name: string;
// 全局函数
declare function say(name: string): void;
// 带属性的对象
declare namespace myObj {
function say(s: string): string;
let money: number;
}
// 可重用的接口
interface Animal {
kind: string;
age: number;
weight?: number;
}
declare function findAnmiate(animal:Animal):void
当然我们还可以定义更多有用的声明, 这里就不一一举例了.
1.使用umi搭建react+typescript项目
为了帮助大家快速上手typescript开发, 这里我们采用umi来搭建一个支持ts的项目, 不熟悉的朋友可以参考笔者之前学习umi的文章.
2.定义去全局声明文件
我们在项目src目录下创建一个global.d.ts来作为我们全局的声明文件, 用来处理全局声明和兼容第三方库.
3.使用ts实现工具类库
我们在src目录下创建一个utils目录用来存放我们的工具类或者通用库, 比如在utils下新建一个tool.ts作为我们的通用工具函数, 这里我们写个最简单的例子:
/**
* 生成uuid
*/
const uuid = ():string => {
let s:Array<any> = [];
let hexDigits:string = "0123456789abcdef";
for (let i = 0; i < 36; i++) {
s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
}
s[14] = "4";
s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1);
s[8] = s[13] = s[18] = s[23] = "-";
let uuid = s.join("");
return uuid;
};
// 可以来自于外部的type.ts文件
interface Params {
[propName: string]: string | number
}
/**
* reverseJson 反转对象键值对
* @param {object} obj 待反转的对象
* @param {object} target 反转的目标对象
*/
const reverseJson = (obj:Params = {}, target:Params = {}):Params => {
Object.keys(obj).forEach((key:string) => { target[obj[key]] = key })
return target
}
以上只是几个简单的案例, 还不够完善, 大家可以根据自己的需求实现相应的封装.
本文由哈喽比特于4年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/mMcdPzKPH0gpVtxg2G5e9Q
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。