本文由IMWeb首发于 IMWeb 社区网站 imweb.io。点击阅读原文查看 IMWeb 社区更多精彩文章。

1. 前言

2018年Stack Overflow Developer的调研()显示,TypeScript已经成为比JavaScript更受开发者喜爱的编程语言了。

赋值运算符_赋值法_jquery给input赋值

之前我其实对于typescript没有太多好感,主要是认为其学习成本比较高,写起代码来还要多写很多类型声明,并且会受到静态类型检查的限制,很不自由,与javascript的设计哲学♂相悖。我相信有很多人也抱持着这样的想法。

然而,最近由于项目需要,学习并使用了一波typescript,结果。。。

赋值运算符_赋值法_jquery给input赋值

2. Typescript是什么?

typescript,顾名思义,就是type + javascript,也就是加上了类型检查的js。官方对于typescript的介绍也指出,typescript是javascript的超集。纯粹的js语法,在typescript中是完全兼容的。但是反过来,用typescript语法编写的代码,却不能在浏览器或者Node环境下直接运行,因为typescript本身并不是Ecmascript标准语法。

3. 为什么要使用Typescript?

很多人坚持javascript而不愿使用typescript的一个很大原因是认为javascript的动态性高,基本不需要考虑类型,而使用typescript将会大大削弱编码的自由度。但实际上,动态性并不总是那么美好的。至少,现在javascript的动态性带来了以下三方面的问题:

代码可读性差,维护成本高。

所谓”动态一时爽,重构火葬场“。缺乏类型声明,对于自己非常熟悉的代码而言,问题不大。但是如果对于新接手或者太长时间没有接触的代码,理解代码的时候需要自行脑补各种字段与类型,如果不幸项目规模比较庞大,也没什么注释,那么你的反应大概会是像这样的:

赋值法_赋值运算符_jquery给input赋值

有了typescript,每个变量类型与结构一目了然,根本无需自行脑补。搭配编辑器的智能提示,体验可谓舒适,妈妈再也不用担心我拼错字段名了。

缺乏类型检查,低级错误出现几率高。

人的专注力很难一直都保持高度在线状态,如果没有类型检查,很容易出现一些低级错误。例如给某个string变量赋值数值,或给对象赋值时候缺少了某些必要字段,调用函数时漏传或者错传参数等。这些看起来很低级的错误,虽然大多数情况下在自测或者测试阶段,都能被验出来,但是总会浪费你的一些时间去debug。

使用typescript,这种情况甚至不会发生,一旦你粗心地赋错值,编辑器立即标红提示,将bug扼杀在摇篮之中。

类型不确定,运行时解析器需要进行类型推断,存在性能问题。

我们知道javascript是边解析边执行的,由于类型不确定,所以同一句代码可能需要被多次编译,这就造成性能上的开销。

虽然typescript现在无法直接解决性能上的问题,因为typescript最终是编译成javascript代码的,但是现在已经有从typescript编译到WebAssembly的工具了:。

好了,如果看完了上面的内容,您还是表示对于typescript不感兴趣,那么后面的内容就可以忽略了哈哈哈。。。

jquery给input赋值_赋值法_赋值运算符

4. Typescript基础篇4.1 基础类型

typescript中的基础类型有:

jquery给input赋值_赋值法_赋值运算符

其中,number、string、boolean、object、null、undefined、symbol都是比较简单的。

例如:

let num: number = 1; // 声明一个number类型的变量let str: string = 'string'; // 声明一个string类型的变量let bool: boolean = true; // 声明一个boolean类型的变量let obj: object = { // 声明一个object类型的变量  a: 1,}let syb: symbol = Symbol(); // 声明一个symbol类型的变量

null和undefined可以赋值给除了never的其他类型。

如果给变量赋予与其声明类型不兼容的值,就会有报错提示。

例如:

赋值运算符_赋值法_jquery给input赋值

Array 数组类型

在typescript中,有两种声明数组类型的方式。

方式一:

let arr: Array<number> = [1, 2, 3]; // 声明一个数组类型的变量

方式二:

let arr: number[] = [1, 2, 3]; // 声明一个数组类型的变量

Tuple 元组类型

元组类似于数组,只不过元组元素的个数和类型都是确定的。

let tuple: [number, boolean] = [0, false];

any类型

当不知道变量的类型时,可以先将其设置为any类型。

设置为any类型后,相当于告诉typescript编译器跳过这个变量的检查,因此可以访问、设置这个变量的任何属性,或者给这个变量赋任何值,编译器都不会报错。

let foo: any;foo.test();foo = 1;foo = 'a';

void类型

通常用来声明没有返回值的函数的返回值类型。

function foo(): void {}

never类型

通常用来声明永远不会正常返回的函数的返回值类型:

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

never与void的区别便是,void表明函数会正常返回,但是返回值为空。never表示的是函数永远不会正常返回,所以不可能有值。

enum 枚举类型

使用枚举类型可以为一组数值赋予友好的名字。

enum Color {Red, Green, Blue}let c: Color = Color.Green;

默认情况下,从0开始为元素编号。你也可以手动的指定成员的数值。例如,我们将上面的例子改成从1开始编号:

enum Color {Red = 1, Green, Blue}let c: Color = Color.Green;

或者,全部都采用手动赋值:

enum Color {Red = 1, Green = 2, Blue = 4}let c: Color = Color.Green;

元素类型也支持字符串类型:

enum Color {Red = 'Red', Green = 'Green', Blue = 'Blue'}let c: Color = Color.Green;

枚举类型提供的一个便利是你可以由枚举的值得到它的名字。例如,我们知道数值为2,但是不确定它映射到Color里的哪个名字,我们可以查找相应的名字:

enum Color {Red = 1, Green, Blue}let colorName: string = Color[2];console.log(colorName);  // 显示'Green'因为上面代码里它的值是2

4.2 类型断言

有点类似其他强类型语言的强制类型转换,可以将一个值断言成某种类型,编译器不会进行特殊的数据检查和结构,所以需要自己确保断言的准确性。

断言有两种形式,一种为尖括号语法,一种为as语法。

尖括号语法:

let someValue: any = "this is a string";let strLength: number = (<string>someValue).length;

as语法:

let someValue: any = "this is a string";let strLength: number = (someValue as string).length;

在大部分情况下,这两种语法都可以使用,但是在jsx中就只能使用as语法了。

5. Typescript进阶篇5.1 函数

函数类型:

函数类型主要声明的是参数和返回值的类型。

function sum(a: number, b: number): number {  return a + b;}

约等于

const sum: (numberA: number, numberB: number) => number = function(a: number, b: number): number {  return a + b;}

注意到类型定义时参数的名称不一定要与实际函数的名称一致,只要类型兼容即可。

可选参数:

函数参数默认都是必填的,我们也可以使用可选参数。

function sum(a: number, b: number, c?: number): number {  return c ? a + b + c : a + b;}

重载:

javascript本身是个动态语言。javascript里函数根据传入不同的参数而返回不同类型的数据是很常见的。

来看个简单但没什么用的例子:

function doNothing(input: number): number;function doNothing(input: string): string;function doNothing(input): any {  return input;}console.log(doNothing(123));console.log(doNothing('123'));

当然也可以使用联合类型,但是编译器就无法准确知道返回值的具体类型。

function doNothing(input: number | string): number | string {  return input;}console.log(doNothing('123').length); // 错误:Property 'length' does not exist on type 'string | number'

如果只是单纯参数的个数不同,返回值类型一样,建议使用可选参数而不是重载。

function sum(a: number, b: number, c?: number) {  return c ? a + b + c : a + b;}

5.2 interface 接口

对于一些复杂的对象,需要通过接口来定义其类型。

interface SquareConfig {  color: string;  width: number;}const square: SquareConfig = {  color: 'red', width: 0,};

可选属性:

默认情况下,每个属性都是不能为空的。如果这么写,将会有报错。

interface SquareConfig {  color: string;  width: number;}const square: SquareConfig = {  color: 'red',};// error

可以将用”?”将width标志位可选的属性:

interface SquareConfig {  color: string;  width?: number;}const square: SquareConfig = {  color: 'red',};

只读属性:

一些对象属性只能在对象刚刚创建的时候修改其值。你可以在属性名前用readonly来指定只读属性。

interface Point {    readonly x: number;    readonly y: number;}

如果在初始化后试图修改只读属性的值,将会报错。

let p: Point = { x: 10, y: 20 };p.x = 20; // error

函数类型:

接口除了能够描述对象的结构之外,还能描述函数的类型。

interface SumFunc {  (a: number, b: number): number;}let sum: SumFunc;sum = (numberA: number, numberB: number) => {  return numberA + numberB;}

可以看到函数的类型与函数定义时只要参数类型一致即可,参数名不一定要一样。

可索引类型:

可索引类型,实际就是声明对象的索引的类型,与对应值的类型。接口支持两种索引类型,一种是number,一种是string,通过可索引类型可以声明一个数组类型。

interface StringArray {  [index: number]: string;}let myArray: StringArray;myArray = ["Bob", "Fred"];let myStr: string = myArray[0];

5.3 class 类

typescript中的类是javascript中类的超集,所以如果你了解es6中的class的语法,也不难理解typescript中class的语法了。

这里主要说下typescript的class和javascript的class的不同之处:

只读属性

类似于接口中的只读属性,只能在类实例初始化的时候赋值。

class User {  readonly name: string;  constructor (theName: string) {  	this.name = theName;  }}let user = new User('Handsome');user.name = 'Handsomechan'; // 错误!name是只读的

public、private、protected修饰符:

public修饰符表示属性是公开的,可以通过实例去访问该属性。类属性默认都是public属性。

class Animal {    constructor(public name: string) {}}const animal = new Animal('tom');console.log(animal.name); // 'tom'

注意在类的构造函数参数前加上修饰符是一个语法糖,上面的写法等价于:

class Animal {  	public name: string;    constructor(name: string) {      this.name = name;    }}

private修饰符表示属性是私有的,只有实例的方法才能访问该属性。

class Animal {  getName(): string { return this.name }  constructor(private name: string) {}}const animal = new Animal('tom');console.log(animal.getName()); // 'tom'console.log(animal.name); // Property 'name' is private and only accessible within class 'Animal'.

protected修饰符表示属性是保护属性,只有实例的方法和派生类中的实例方法才能访问到。

class Animal {  constructor(public name: string, protected age: number) {}}class Cat extends Animal {  getAge = ():number => {    return this.age;  }}const cat = new Cat('tom', 1);console.log(cat.getAge()); // 1

抽象类:

抽象类做为其它派生类的基类使用。它们一般不会直接被实例化。不同于接口,抽象类可以包含成员的实现细节。abstract关键字是用于定义抽象类和在抽象类内部定义抽象方法。

abstract class Animal {    abstract makeSound(): void; // 抽象方法,必须在派生类中实现    move(): void {        console.log('roaming the earch...');    }}class Sheep extends Animal {  makeSound() {    console.log('mie~');  }}const animal = new Animal(); // 错误,抽象类不能直接实例化const sheep = new Sheep();sheep.makeSound();sheep.move();

实现接口:

类可以实现一个接口,从而使得类满足这个接口的约束条件。

interface ClockInterface {    currentTime: Date;}class Clock implements ClockInterface {    currentTime: Date;    constructor(h: number, m: number) { }}

5.4 泛型

泛型在强类型语言中很常见,泛型支持在编写代码时候使用类型参数,而不必在一开始确定某种特定的类型。这样做的原因有两个:

有时候没办法在代码被使用之前知道类型。

例如我们封装了一个request函数,用来发起http请求,返回请求响应字段。

我们在实现request函数的时候,实际上是不能知道响应字段有哪些内容的,因为这跟特定的请求相关。

所以我们将类型确定的任务留给了调用者。

// 简单封装了一个request函数async function request<T>(url: string): Promise<T> {  try {    const result = await fetch(url).then((response) => {      return response.json();    });    return result;  } catch (e) {    console.log('request fail:', e);    throw e;  }}async function getUserInfo(userId: string): void {  const userInfo = await request<{    nickName: string;    age: number;  }>(`user_info?id=${userId}`)  console.log(userInfo); // { nickName: 'xx', age: xx }}getUserInfo('123');

提高代码的复用率。

如果对于不同类型,代码的操作都是一样的,那么可以使用泛型来提高代码的复用率。

// 获取数组或者字符串的长度function getLen<T extends Array<any> | string>(arg: T): number {  return arg ? arg.length : 0;}

当然,您可能觉得这两点在javascript中都可以轻易做到,根本不需要泛型。是的,泛型本身是搭配强类型食用更佳的,在弱类型下没意义。

在typescript中,泛型有几种打开方式:

泛型函数:

function someFunction<T>(arg: T) : T {  return arg;}console.log(someFunction<number>(123)); // 123

泛型类型:

泛型类:

class UserInfo {  constructor(private id: T, private age: number) {};  getId(): T {    return this.id;  }}

我们也可以给类型变量加上一些约束。

泛型约束

有时编译器不能确定泛型里面有什么属性,就会出现报错的情况。

function logLength<T>(arg: T): T {    console.log(arg.length);  // Error: T doesn't have .length    return arg;}

解决方法是加上泛型约束。

interface TypeWithLength {  length: number,}function logLength<T extends TypeWithLength>(arg: T): T {  console.log(arg.length);  // ok  return arg;}

6. Typescript高级篇6.1 高级类型

交叉类型:

交叉类型是将多个类型合并为一个类型。

interface typeA {  a?: number,}interface typeB {  b?: number,}let value: typeA & typeB = {};value.a = 1; // okvalue.b = 2; // ok

联合类型:

联合类型表示变量属于联合类型中的某种类型,使用时需要先断言一下。

interface TypeA {  a?: number,}interface TypeB {  b?: number,}const value: TypeA | TypeB = {};(value).a = 1; // ok

6.2 类型别名 type

类型别名可以给一个类型起个新名字。类型别名有时和接口很像,但是可以作用于原始值,联合类型,元组以及其它任何你需要手写的类型。可以将type看做存储类型的特殊类型。

type Name = string;type NameResolver = () => string;type NameOrResolver = Name | NameResolver;...

6.3 is

is关键字通常组成类型谓词,作为函数的返回值。谓词为parameterName is Type这种形式,parameterName必须是来自于当前函数签名里的一个参数名。

function isFish(pet: Fish | Bird): pet is Fish {    return (pet).swim !== undefined;}

这样的好处是当函数调用后,如果返回true,编译器会将变量的类型锁定为那个具体的类型。

例如:

if (isFish(pet)) {  pet.swim(); // 进入这里,编译器认为pet是Fish类型。} else {  pet.fly(); // 进入这里,编译器认为pet是Bird类型。}

6.4 keyof

keyof为索引类型查询操作符。

interface Person {    name: string;    age: number;}type IndexType = keyof Person; // 'name' | 'age'

这样做的好处是使得编译器能够检查到动态属性的类型。

function pick<T, K extends keyof T>(obj: T, keys: K[]): T[K][] {  return keys.map(key => obj[key]);}console.log(pick(person, ['name', 'age'])); // [string, number]

限时特惠:本站每日持续更新海量各大内部网赚创业教程,会员可以下载全站资源点击查看详情
站长微信:11082411

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。