TypeScript

强类型与弱类型

类型安全纬度:强类型与弱类型
强类型:语言层面限制函数的形参类型和实参类型必须相同。
弱类型:语言层面不会限制实参的类型。
强弱类型之分,根本不是某一个权威机构的定义,普遍来说,强类型语言中,不允许任意的隐式类型转换,而弱类型则允许
js的类型错误提示,都是在运行层面,通过逻辑判断手动抛出的错误,而不是语言层面

静态型与动态型

类型检查纬度:静态型与动态型
静态型:一个变量声明时,它的类型就是明确的,声明过后,他的类型就不允许再修改。
动态型:运行阶段才能够明确一个变量的类型,而且变量的类型可以随时发生变化,动态类型的语言中变量没有类型,变量当中存放的值是有类型的

JS类型系统特征

弱类型/动态类型,js几乎没有任何的类型限制(任性),灵活多变,但缺失了类型系统的可靠性(不靠谱)
js设计的初衷就是简单的需求,一眼到头的代码,不需要强类型的限制,js是脚本语言,没有编译环节,而静态类型的类型检查就是在编译环节,所以弱类型/动态类型,是它当时的一大优势。
在现如今大规模的js应用下,它的优势反而变成了短板。

弱类型的问题

JavaScript 弱类型产生的问题

// 1. 异常需要等到运行时才能发现
// const obj = {}
// obj.foo()

// 2. 函数功能可能发生改变
function sum (a, b) {
  return a + b
}
console.log(sum(100, 100)) // => 200
console.log(sum(100, '100')) // => '100100'

// 3. 对象索引器的错误用法
const obj = {}
obj[true] = 100 // 属性名会自动转换为字符串
console.log(obj['true'])

强类型的优势

// 强类型的优势
// 1. 强类型代码错误更早暴露
// 2. 强类型代码更智能,编码更准确
// =================================
// 3. 重构更可靠
// const util = {
//   aaa: () => { // 修改名称aaa => say,会在编译阶段把引用部分的报错抛出来
//     console.log('hello')
//   }
// }
// =================================
// 4. 减少了代码层面的不必要的类型判断
function sum (a, b) {
  if (typeof a !== 'number' || typeof b !== 'number') {
    throw new TypeError('arguments must be a number')
  }
  return a + b
}

Flow

Flow只是个小工具,很Easy,几乎无学习成本,而TypeScript是一门全新的语言
Flow

TypeScript

TS是JS的超集(扩展集),是一门完整的语言


TypeScript

TS相比Flow,功能更强大,生态更完善,Vue3已经在用TS。
上面说了很多JS的缺点,同样,TS的缺点是:1.语言本身多了很多概念,好在他是渐进式的,意思是你可以完全把它当作JS来写,了解一个特性就可以使用一个特性。2.项目初期TS会增加一些成本。
不过以后前端的发展,TS应该是一门必要的语言了

基本数据类型

"use strict";
// 原始数据类型
var a: string = 'foobar';
var b: number = 100; // NaN Infinity
var c: boolean = true; // false
// 在非严格模式(strictNullChecks)下,
// string, number, boolean 都可以为空(null, undefined)
// const d: string = null
// const d: number = null
// const d: boolean = null
const e: void = undefined // void: undefined || null,严格模式下只能为undefined
const f: null = null
const g: undefined = undefined
// Symbol 是 ES2015 标准中定义的成员,
// 使用它的前提是必须确保有对应的 ES2015 标准库引用
// 也就是 tsconfig.json 中的 lib 选项必须包含 ES2015
const h: symbol = Symbol()

tsconfig.json

"target": "es5",       // 编译后版本                                 
"lib": ["ES2015", "DOM"], // 标准库,标准库就是内置对象所对应的声明
"strict": true,  // 严格模式

作用域问题

// 作用域问题

// 默认文件中的成员会作为全局成员
// 多个文件中有相同成员就会出现冲突
// const a = 123

// 解决办法1: IIFE 提供独立作用域
// (function () {
//   const a = 123
// })()

// 解决办法2: 在当前文件使用 export,也就是把当前文件变成一个模块
// 模块有单独的作用域
const a = 123
export {}

Object类型

// object 类型是指除了原始类型以外的其它类型
const foo: object = function () {} // [] // {} //函数

// 如果需要明确限制对象类型,则应该使用这种类型对象字面量的语法,或者是「接口」
const obj: { foo: number, bar: string } = { foo: 123, bar: 'string' }
// 这么定义,那不能多也不能少

// 更专业的方法是接口,接口的概念后续介绍

数组类型

// 数组类型的两种表示方式
const arr1: Array<number> = [1, 2, 3] // 都表示全部由数字组成的数组
const arr2: number[] = [1, 2, 3]
// 案例 -----------------------
// 如果是 JS,需要判断是不是每个成员都是数字
// 使用 TS,类型有保障,不用添加类型判断
function sum (...args: number[]) {
  return args.reduce((prev, current) => prev + current, 0)
}
sum(1, 2, 3) // => 6

元组类型

元组:明确元素数量及类型的数组

const tuple: [number, string] = [18, 'zce']
// const age = tuple[0]
// const name = tuple[1]
const [age, name] = tuple
// ---------------------
const entries: [string, number][] = Object.entries({
  foo: 123,
  bar: 456
})
const [key, value] = entries[0]
// key => foo, value => 123

枚举类型

TS加入了枚举类型

// 标准的数字枚举
enum PostStatus {
  Draft = 0,
  Unpublished = 1,
  Published = 2
}
// 数字枚举,枚举值自动基于前一个值自增
enum PostStatus2 {
  Draft = 6, // 如果第一个都没有值的话,就默认是0
  Unpublished, // => 7
  Published // => 8
}
console.log(PostStatus2.Published) // => 8
console.log(PostStatus[0]) // => Draft  
// 常量枚举,不会侵入编译结果
const enum PostStatus { // 加const
  Draft,
  Unpublished,
  Published
}

函数类型

function func1 (a: number, b: number, ...rest: number[]): string {
  return 'func1'
}
func1(100) // 报错
func1(100, 200, 300) // 报错  形参,实参的【个数/类型】必须完全一致
function func2 (a: number, b?: number = 10){} // ?代表可选,或者添加默认值也代表可选
function func3 (a: number, ...rest: number[]){} // 任意个数的参数
// 第二种写法=========
const func2: (a: number, b: number) => string = function (a, b) {
    return 'func2'
}

任意类型

function stringify (value: any) {
  return JSON.stringify(value)
}
let foo: any = 'string'
// any 类型是不安全的

隐式类型判断

let age = 18 // ts会推断他为number类型 相当于 let age: number = 18
age = 'string' // 这时就会报错

let foo //这时TS无法推断其类型,相当于 let foo: any;
foo = 100 // 不会报错
foo = 'string'
// 建议为每个变量添加明确的类型标注

类型断言

// 假定这个 nums 来自一个明确的接口
const nums = [110, 120, 119, 112]
const res:number = nums.find(i => i > 0) 
// 它会被ts推断成number |undefined 但是我们明确知道它一定是数字
// 所以可以用以下两种断言方式
const num1 = res as number
const num2 = <number>res // JSX 下不能使用

接口

声明对象类型为例,用接口约束对象的结构

// 声明对象类型为例,用接口约束对象的结构
// 定义接口的关键字interface
interface Post {
  title: string
  content: string
  eat (food: string): void
}
function printPost (post: Post) { // 注意这里 :Post
  console.log(post.title)
  console.log(post.content)
}
printPost({  // 入参要根据interface,必须每个属性都要有
  title: 'Hello TypeScript',
  content: 'A javascript superset'
  eat: ()=>console.log('吃')
})
// 接口,可选成员
interface Post {
  title: string
  content?: string // 可有可无 相当于content:string | undefined
}
// 只读成员
interface Post {
  title: string
  subtitle?: string
  readonly summary: string // readonly
}
const hello: Post = {
  title: 'Hello TypeScript',
  content: 'A javascript superset',
  summary: 'A javascript'
}
hello.summary = 'other' // summary被定义一次后,无法修改,会报错

// 动态成员
interface Cache {
    [prop: string]: string | Array<any>
}
const cache: Cache = {}
cache.foo = 'value1'
cache.bar = 'value2'

类:描述一类具体事务的抽象特征

class Person {
  name: string // = 'init name'  
  age: number
// TS里类的初识类型必须要在这里定义,而不是在constructor里
 
  constructor (name: string, age: number) {
    this.name = name  // TS里类的变量也必须要有初始值
    this.age = age
  }
  sayHi (msg: string): void {
    console.log(`I am ${this.name}, ${msg}`)
  }
}

类的访问修饰符

class Person {
  public name: string // 默认就是public,所以可以不加
  private age: number // 私有属性
  protected gender: boolean // 受保护类型
  public readonly height: number // readonly只能跟在访问修饰符的后面
  
  constructor (name: string, age: number) {
    this.name = name
    this.age = age
    this.gender = true
    this.height = 180
  }

  sayHi (msg: string): void {
    console.log(`I am ${this.name}, ${msg}`)
    console.log(this.age)
  }
}
const tom = new Person('tom', 18)
tom.height = 170 // 无法修改
console.log(tom.age) // 类的私有属性,无法在实例中访问
console.log(tom.gender) // 类的受保护属性也无法在实例中访问
// 那他俩有啥区别呢

class Student extends Person {
  constructor (name: string, age: number) {
    super(name, age)
    console.log(this.age) // 不可访问,私有属性仅仅在类自己内部可访问
    console.log(this.gender) // 可以访问,受保护属性在子类中可访问
  }
}

类与接口

interface Eat {
  eat (food: string): void
}

interface Run {
  run (distance: number): void
}

class Person implements Eat, Run {
  eat (food: string): void {
    console.log(`优雅的进餐: ${food}`)
  }
  run (distance: number) {
    console.log(`直立行走: ${distance}`)
  }
}

class Animal implements Eat, Run {
  eat (food: string): void {
    console.log(`呼噜呼噜的吃: ${food}`)
  }
  run (distance: number) {
    console.log(`爬行: ${distance}`)
  }
}

泛型

function createArray<T> (length: number, value: T): T[] {
    const arr = Array<T>(length).fill(value)
    return arr
}

const res = createArray<string>(3, 'foo') // 指定类型
console.log(res)
// 我们换用any============================================================

function createAnyArray (length: number, value: any): any[] {
    const arr = Array<any>(length).fill(value)
    return arr
}
const res2 = createAnyArray(3, 'any')
console.log(res2)

如果你使用 any 的话,怎么写都是 ok 的, 但这就丧失了类型检查的效果,createAnyArray(1,2).toFixed也不会报错,因为TS判断不出是什么类型,但是调用泛型时会明确数据类型,所以TS就会类型检查,及时报错

类型声明

比如一些第三方库(Lodash),没有类型声明,那么引用lodash的方法时,就没有变量类型提示

import { camelCase } from 'lodash'

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