TypeScript入门学习@郝晨光


前言

现在TypeScript越来越火,咱也赶一下潮流,开始学习一下TypeScript,在学习的同时做笔记记录,希望可以共同学习,在未来的程序生涯中可以使用TypeScript来进行开发。

TypeScript是什么?

TypeScript 是 JavaScript 的一个超集,主要提供了类型系统对 ES6 的支持,因为JavaScript是弱类型语言,我们在开发的时候,很容易会因为数据类型的原因造成一些不明不白的错误,所以,在TypeScript中,对类型的检测是极为严格的。

TypeScript的优势

在这里我借用阮一峰大神的文章来说一下TypeScript的优势。

  1. TypeScript 增加了代码的可读性和可维护性;

    • 类型系统实际上是最好的文档,大部分的函数看看类型的定义就可以知道如何使用了
    • 可以在编译阶段就发现大部分错误,这总比在运行时候出错好
    • 增强了编辑器和 IDE 的功能,包括代码补全、接口提示、跳转到定义、重构等
  2. TypeScript 非常包容;

    • TypeScript 是 JavaScript 的超集,.js 文件可以直接重命名为 .ts 即可
    • 即使不显式的定义类型,也能够自动做出类型推论
    • 可以定义从简单到复杂的几乎一切类型
    • 即使 TypeScript 编译报错,也可以生成 JavaScript 文件
    • 兼容第三方库,即使第三方库不是用 TypeScript 写的,也可以编写单独的类型文件供 TypeScript 读取
  3. TypeScript 拥有活跃的社区;

    • 大部分第三方库都有提供给 TypeScript 的类型定义文件
    • Google 开发的 Angular2 就是使用 TypeScript 编写的
    • TypeScript 拥抱了 ES6 规范,也支持部分 ESNext 草案的规范

TypeScript的缺点

  • 无非就是难学了一点,无所谓啦,反正再难学还是要学的!自己选的路,跪着也要走下去。

写在前边

首先一定要从JavaScript的弱类型思想中走出来,在TypeScript中,保证在大部分时候都要给数据定义类型。
其次一定要坚持,坚持,坚持。
那就先从基础开始吧!


安装使用

  1. 首先全局安装typescript
    npm install typescript -g
    
  2. 当全局安装后,我们就可以在全局下使用tsc指令了
    先通过tsc -v查看一下当前的TypeScript的版本号,顺便看一下有没有安装成功。
    tsc -v
    
  3. 接着新建一个文件夹,新建一个.ts文件,例如: index.ts;
    在使用TypeScript时,我们使用.ts为后缀的文件,如果是使用React的时候,我们默认使用.tsx为后缀的文件。它们分别对应的是.js.jsx文件。
  4. index.ts文件中,写入如下代码
    function sayHello(person: string) {
        return 'Hello' + person;
    }
    let person = [1, 2, 3];
    sayHello(person);
    
  5. 打开命令提示符,通过如下指令编译TypeScript文件
    tsc index.ts
    
  6. 执行会发现命令提示符报错index.ts:5:10 - error TS2345: Argument of type 'number[]' is not assignable to parameter of type 'string',因为我们给函数的参数定义成为string类型,而我们传入的是一个数组,所以会报错。
  7. 但是虽然报错了,依旧生成了index.js文件,这是因为TypeScript文件就算编译出错也依旧会编译成js文件,毕竟这点错误在咱们的JavaScript里边不算什么。
  8. 接着修改我们的index.ts文件
    function sayHello(person: string) {
        return 'Hello' + person;
    }
    let person: string = '郝晨光';
    sayHello(person);
    
  9. 再次执行tsc index.ts指令
  10. 这次命令提示符没有任何的错误信息,并且可以看到生成的index.js文件。

基本类型

对于基本类型,直接定义类型即可。定义好就必须指定类型,不可以变成其它类型。

  1. 定义布尔类型的值
    // 布尔值
    let isDone: boolean = true;
    
  2. 定义数字类型的值
    // 数字
    let count: number = 1;
    
  3. 定义字符串类型的值
    // 字符串
    let str:string = 'typescript';
    
  4. 定义空值
    空值是typescript中新引入的一种基本类型,可以用 void 表示没有任何返回值的函数。
    function alertName(): void {
        alert('郝晨光加油!')
    }
    
    或者声明一个空值,但是这样毫无意义,因为只能被赋值为undefined和null。
    let myVoid1: void = undefined;
    let myVoid2: void = null;
    
  5. 定义null和undefined
    // null
    let nullSet: null = null;
    // undefined
    let undeSet: undefined = undefined;
    
    需要注意的是,null和undefined可以被赋予任何类型,但是void的空值不能被赋予其他类型。
    let unsetNumber: number = null;
    let unsetString: string = undefined;
    
  6. 任意类型
    我们都非常熟悉JavaScript,在JavaScript中,我们的变量是可以随时转换数据类型的,但是在TypeScript中,对于定义好的基本数据类型是不可以的!那我们如何实现一个不一定是什么数据类型的值呢?就该使用任意类型了。
    // 任意类型
    // 对于任意类型,可以在赋值后修改数据类型。
    let myAny: any = 'string';
    myAny = 123;
    // 变量在声明的时候没有指定类型,并且没有赋值,自动编译为 any 类型
    let anyThing;
    anyThing = '未指定类型并且未赋值,自动编译为 any 类型';
    anyThing = 1234;
    

类型推断

了解了基本数据类型,接下来就应该先了解一下TypeScript的类型推断,在TypeScript中,如果你没有给(变量/数据)指定一个数据类型,分为两种情况,

  1. 一种是定义未赋值,就是我们在上边的任意类型中提到的,它会自动声明成为any类型。
  2. 而如果我们在定义(变量/数据)的时候,给它赋值,但是不给它指定类型的话,TypeScript就会进行类型推断。
    // 但是如果变量在声明是赋值了,但是没有指定类型
    // 经过类型推断,它应该是赋值时候的数据类型,即为string。
    let stringThing = '未指定类型,但是赋值为string';
    stringThing = 1234; // 报错
    
    在上边的案例中,在定义stringThing变量定义时,给它赋值成为string,经过TypeScript的类型推断后,它会被设置成为string数据类型,而当我们给它赋值成为number类型时,TypeScript就会进行报错。

联合类型

我们在实际开发中经常会遇到这样的问题,一个参数,它既有可能是string类型,又有可能是number类型,那这种情况,如果我们用TypeScript应该怎么定义呢?肯定不可能使用any吧,那样的话,我们还可以设置其他类型的值。所以,就出现了联合类型。

// 联合类型(多个类型)
let numString: string | number = '一开始是string';
numString = 123456789; // 可以被赋值为number类型
numString = true; // 报错,不可以是boolean类型。

但是,针对联合类型,只能使用它们之间公共的方法,不能使用其中一个特有的方法。例如:

// 由于number是没有length属性的,所以会报错
function getLength(some: string | number): number {
     return some.length;
}

访问number和string的公共属性没有任何问题,不会报错。

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

联合类型会在被赋值的时候推断出一个类型,并监听除当前数据类型以外的错误。

let myName: string | number;
myName = '郝晨光';
// 此时是字符串类型,所以调用length属性没有任何问题
console.log(myName.length); // 3
myName = 123;
// 此时是number类型,没有lenght属性,所以会报错
console.log(myName); // error TS2339: Property 'length' does not exist on type 'number'.

对象的类型

在ts中,通过接口(interface)来定义对象的类型;

  1. 接口定义了对象的值的类型,并且定义了对象的参数,多一个参数少一个参数都不可以。

    interface Person {
        name: string,
        age: number,
        sex: string
    }
    
    let person: Person = {
        name: '郝晨光',
        age: 24,
        sex: '男'
    };
    
  2. 当我们的对象有一些属性不是必须的时候,可以通过?来定义,?表示属性是可选的。

    interface People {
        name: string,
        age: number,
        sex?: string
    }
    
    let girl: People = {
        name: 'girl',
        age: 20
    };
    
  3. 当我们的对象有一些未知的属性时,可以通过[propName]来定义

    interface Student {
        name: string,
        age?: number,
        [propName: string]: string | number
    }
    let student: Student = {
        name: '学生',
        age: 20,
        className: 'A班'
    };
    
    1. 如果设置了propName的话,可选的值(带?的值),就必须是propName的指定值的子类型
    2. 例如any,所有类型都是它的子类型,或者通过联合类型来指定
    3. 在上边的案例中,如果我只给propName指定数据类型为string的话,TypeScript就会报错,因为age属性需要的是number类型的值。
  4. 我们还可以给对象设置只读的属性。

    // 只读属性
    // 通过 readonly 属性来设置属性是只读的
    interface Teacher {
        readonly id: number,
        name: string,
        age?: number,
        [propName: string]: any
    }
    let teacher: Teacher = {
        id: 123456,
        name: '郝晨光',
        age: 24,
        className: 'A班'
    };
    console.log(teacher.id);
    teacher.id = 1231546; // 报错,因为是只读的,不能修改
    

数组的类型

在TypeScript中定义数组的方式有很多

  1. 类型 + 方括号
    let arr1: number[] = [1, 2, 3];
    // 如果出现非number的值,就会报错
    arr1 =  [1, 2, '1', 3]; // 报错
    arr1.push(10); // 正常
    arr1.push('10'); // 报错
    
  2. 数组泛型
    let arr2: Array<number> = [1, 2, 3];
    arr2 = [1, 2, '1', 3]; // 报错;
    arr2.push(10); //正常
    arr2.push('10'); // 报错
    
  3. 使用接口interface定义
    interface NumberArray {
        [index: number]: number // 定义数据是number类型
    
        push(number: number): void; // 定义push方法接收number类型的值,并返回空值
    }
    let arr3: NumberArray = [1, 2, 3];
    arr3 = [1, 2, '1', 3]; // 报错;
    arr3.push(10); // 正常
    arr3.push('10'); // 报错
    
  4. 使用any定义数组
    // 数组中允许出现任意类型的值
    let arr4: any[] = [1, '2', new Date(), 3];
    
  5. 类数组
    类数组不能通过数组的方式定义,每一个类数组都有它对应的内置对象
    arguments ==> IArguments
    NodeList ==> NodeList
    HTMLCollection ==> HTMLCollection
    ···
    

函数的类型

在JavaScript中,定义函数有两种方式,一种是函数声明式,另一种是函数表达式。
在函数定义之后,输入多余的(或者少于要求的)参数,是不被允许的。

  1. 函数声明式
    function fn(x: number, y: number): number {
        return x + y;
    }
    
  2. 函数表达式
    注意:这样声明对于左边的变量来说,并没有任何数据指定,虽然不会报错,但是是不严谨的
    let fn2 = function(x: number, y: number): number {
        return x + y;
    };
    
    所以我们应该这样定义。
    let fn3: (x: number, y: number) => number = function(x: number, y: number): number {
        return x + y;
    };
    
    需要注意的是TypeScript中,用在此处的 => 与 ES6中的箭头函数是没有任何关系的,此处只是为了指定返回值的数据类型。
  3. 使用接口interface定义函数
    interface Fun {
        (x: number, y: number): number
    }
    // 此时可以不指定右边的函数的返回值的类型,因为在接口中定义了
    let fn4: Fun = function(x: number, y: number) {
        return x + y;
    };
    
  4. 可选参数
    function fn5(firstName: string, lastName?: string): string {
        return firstName + ' ' + lastName;
    }
    
    需要注意的是,可选参数必须出现在最后,不能在可选参数之后又出现必须参数。
    下面的案例会报错!
    function fn6(x?: number,y: number): number {
        return x + y;
    }
    
    函数出现多个可选参数
    // 出现多个可选参数
    function fn7(x: number,y?: number, z?: number): number {
        return x + y + z;
    }
    
  5. 参数默认值
    当定义了默认值时,可选参数将不会在受限制,可以出现在必须参数前边,调用时直接传入null或者undefined即可。
    function fn8(x: number = 5,y: number):number {
        return x + y;
    }
    fn8(null,10); // 15
    fn8(10, 20); // 30
    
  6. 剩余参数
    在ES6中,可以通过扩展运算符来接收剩余的参数,剩余参数其实就是一个数组,所以可以通过any[]或者Array<any>等等方式定义类型。
    function fn9(arr: any[], ...args: any[]): Array<any> {
        args.forEach(item => {
            arr.push(item);
        });
        return arr;
    }
    
  7. 函数重载
    重载就是指函数在接收不同类型的参数或者不同数量的参数时,做出不同的处理,返回不同的结果。
    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(x === 'string'){
            return x.split('').reverse().join('');
        }
    }
    reverse(123456);
    reverse('123456');
    

本文参考:TypeScript入门教程 - 阮一峰

如果本文对您有帮助,可以看看本人的其他文章:
简单实现Vue响应式原理@郝晨光
前端常见面试题(十六)@郝晨光
前端常见面试题(十五)@郝晨光

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

推荐阅读更多精彩内容