TypeScript学习(基础篇一)

数据类型

js的数据类型有七种:Boolean,Number,Object,Array,null,undefined,Symbol

类型

这里就说几个比较特别需要注意的点

空值(Void)

在js中没有空值的概念,在ts中,可以用void表示没有任何返回值的函数

function alertName():void {
    alert("my name is lhy");
}

然而声明一个void类型的变量没有什么用,因为你只能将它赋值为null和undefined。

let undisable: void = undefined;

任意类型(any)

如果是一个普通的类型,在赋值过程中改变类型是不允许的,但是任意类型是可以的

let anything: any = {} | null |'' | 0

声明了一个变量为任意值之后,对它的任何操作和返回的内容的类型都是任意值。

并且一个变量如果没有声明任何一个类型,那么它默认就是任意类型。(但是我瞅着好像在我代码里面并不是这样)

联合类型

联合类型使用 | 来分割每个类型。

let test: string | number

访问联合属性的方法:我们必须只能访问此联合类型里公用的属性和方法

错误:

function getLength(something: string | number): number {
    return something.length;
}

number没有length这个属性

正确:

function getString(something: string | number): string {
    return something.toString();
}

所以,联合类型在被赋值的时候,会根据类型推论的规则推断出一个类型。

对象的类型-interface(接口)

TS中interface是一个很灵活的概念,除了可以用于对类的一部分行为进行抽象以外,也常用于对“对象的形状”进行描述

规范:接口一般首字母大写,有的编程语言中会建议接口的名称加上I前缀

interface IPerson {
    name: string;
    age: number;
}

let tom: IPerson = {
    name: 'Tom',
    age: 25
};

这种的接口,则要求赋值的时候,变量的形状必须和接口的形状保持一直,如果想要拥有任意的未添加定义的属性,可以👇

可选属性/任意属性

interface Person {
    name: string;
    age?: number;
    [propName: string]: any;
}

这个样子变量就可以拥有任意的未添加的定义属性

只读属性

有的时候我们希望对象中的一些字段只能在创建的时候被赋值,那么可以用readonly只读属性。

interface IPerson {
    readonly id: number;
    test: string;
}
let person: IPerson {
        id: 123;
    test: 'xxx'
}
// 这个时候试图去改变id的值则会报错
person.id = 90; 
// 报错!!!

只读的约束存在于第一次给对象赋值的时候,而不是第一次给只读属性赋值的时候

数组的类型

最简单的使用方法就是类型+方括号来表示数组:

let test:number[] = [1, 2, 3, 4]
//  这个数组里面只能是数字

数组泛型( 常用)

let test: Array<number> = [1, 2, 4];

用接口来表示数组

interface NumberArray {
    [index: number]: number
}
let fibonacci: NumberArray = [1, 1, 2, 3, 5];

函数的类型

函数是js里面最重要的成员之一!!

一个函数有输入和输出,要在ts中对其进行约束,则需要把输入和输出都考虑到,最简单的例子:

let mySum = function (x: number, y: number): number {
    return x + y;
};
let mySum: (x: number, y: number) => number = function (x: number, y: number): number {
    return x + y;
};

利用接口定义函数的形状

interface SearchFunc {
    (source: string, subString: string): boolean;
}

let mySearch: SearchFunc;
mySearch = function(source: string, subString: string) {
    return source.search(subString) !== -1;
}

此外,对于函数而言,可选参数后面不允许再出现必须参数了

错误:

function buildName(firstName?: string, lastName: string) {
    if (firstName) {
        return firstName + ' ' + lastName;
    } else {
        return lastName;
    }
}
// error!!!!!

但是如果我们给参数设置了默认值,那么就不受这个规则的限制了!!

剩余参数

ES6中,可以使用...reset 的方式获取函数中的剩余参数。举例如下:

function push(array, ...items) {
    items.forEach(function(item) {
        array.push(item);
    });
}

let a = [];
push(a, 1, 2, 3);

函数重载

重载允许一个函数接受不同数量或类型的参数,作出不同的处理。

比如我们需要实现一个reverse函数,输入数字 123 的时候,输出反转的数字 321,输入字符串 'hello' 的时候,输出反转的字符串 'olleh'。

利用联合类型,我们可以这么实现:

function reverse(x: number | y: number): number | string {
if (typeof x === 'number') {
        return Number(x.toString().split('').reverse().join(''));
    } else if (typeof x === 'string') {
        return x.split('').reverse().join('');
    }
}

但是这样写的话,代码的可读性并不是很高,就是输入数字的时候,输出也应该是数字,输入字符串的时候,输出也应该是字符串,这个时候我们可以用重载去定义reverse函数

function reverse(x: number): number;
function reverse(x: string): string;
function reverse(x: number | string): number | string {
    if (typeof x === 'number') {
        return Number(x.toString().split('').reverse().join(''));
    } else if (typeof x === 'string') {
        return x.split('').reverse().join('');
    }
}

在代码中我们可以看见,我们重复定义了多次函数 reverse,前几次都是函数定义,最后一次是函数实现。

类型断言(便捷,不必使用typeof 判断)

之前有说过,当 TypeScript 不确定一个联合类型的变量到底是哪个类型的时候,我们只能访问此联合类型的所有类型里共有的属性或方法,而有时候,我们确实还是需要在还不确定类型的时候就访问其中一个类型的属性或方法,比如

function getLength(something: string | number): number {
    if (something.length) {
        return something.length;
    } else {
        return something.toString().length;
    }
}

此时会报错!!!

/ index.ts(2,19): error TS2339: Property 'length' does not exist on type 'string | number'.

// Property 'length' does not exist on type 'number'.

// index.ts(3,26): error TS2339: Property 'length' does not exist on type 'string | number'.

// Property 'length' does not exist on type 'number'.

这个时候我们可以非常方便的使用类型断言

function getLength(something: string | number): number {
// 在需要断言的变量前面增加类型!!!!! 避免写 typeof something === 'string' && something.length 这么复杂了!!!
    if ((<string>something).length) {
        return (<string>something).length;
    } else {
        return something.toString().length;
    }
}

声明文件

通常我们会把声明语句放在一个单独的文件里,这就是声明文件,例如创建一个声明文件jQuery.d.ts,里面的声明语句如下:

declare var JQuery:(selector: string) => any;

一般来说,ts会解析项目中所有的.ts文件,当然也包含以.d.ts结尾的文件,所以当我们将 jQuery.d.ts 放到项目中时,其他所有 *.ts 文件就都可以获得 jQuery 的类型定义了。

/path/to/project
├── src
|  ├── index.ts
|  └── jQuery.d.ts
└── tsconfig.json

加入仍然无法解析,那么可以检查下tsconfig.json里面的file, include和exclude的配置。确保其包含了jQuery.d.ts文件。

第三方声明(经常用到)

当然,jQuery 的声明文件不需要我们定义了,社区已经帮我们定义好了:jQuery in DefinitelyTyped

我们可以直接下载下来使用,但是更推荐的是使用 @types 统一管理第三方库的声明文件。

@types 的使用方式很简单,直接用 npm 安装对应的声明模块即可,以 jQuery 举例:

npm install @types/jquery --save-dev

可以在这个页面搜索你需要的声明文件。在项目里面,基本上都会用到第三方声明文件。

但是当一个第三方库没有提供我们想要的声明文件的时候,我们需要去手动书写声明文件,这样就得了解声明语句了。👇

手写声明文件

理论上来说,会在项目里面建立一个types文件夹,然后将我们所有的声明文件放在该目录下,然后这种方式需要配置tsconfig.json中的paths和bathUrl字段。

tsconfig.json的配置:

{
    "compilerOptions": {
        "module": "commonjs",
        "baseUrl": "./",
        "paths": {
            "*": ["types/*"]
        }
    }
}

内置对象

JS提供的标准对象有:Boolean, error, Date, RegExp等

我们可以在ts中将变量定义为这些类型:

let b: Boolean = new Boolean(1);

let e: Error = new Error('Error occurred');

let d: Date = new Date();

let r: RegExp = /[a-z]/;

DOM和BOM的内置对象

DOM 和 BOM提供的内置对象有:

Document, HTMLElement, Event, NodeList等。

TS中会经常用到这些类型:

let body: HTMLElement = document.body;
let allDiv: NodeList = document.querySelectorAll('div');
document.addEventListener('click', function(e: MouseEvent){
 // do something
})

这些类型的定义文件都在ts核心库定义文件

TS核心库定义文件

TS核心库中的定义文件中定义了所有浏览器环境需要用到的类型,并且是预置在TS中的。

当你在使用一些常用方法的时候,TS实际上已经帮你做了很多类型判断的工作。比如:

Math.pow(10, '2');

// index.ts(1,14): error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.
就是这个函数的两个参数必须都是Number类型

想要用TS写node.js

Node.js不是内置对象的一部分,如果想要用Node.js写TS,则需要引入第三方声明文件:

npm install @types/node --save

参考:https://ts.xcatliu.com/engineering/lint

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269