时间:2022-07-21 11:09:42 | 栏目:JavaScript代码 | 点击:次
Typescript是一门基于JavaScript之上的语言,重点解决了JavaScript自由类型系统的问题。
使用Typescript可以大大提高代码的可靠程度。
强类型:语言层面限制函数的实参类型必须与形参类型相同
弱类型:语言层面不会限制实参的类型
由于这种强弱类型之分根本不是某一个权威机构的定义,所以导致后人对这种鉴定方式的细节,出现了不一致的理解。但整体上大家的鉴定方式都是在描述强类型有更强的类型约束,而弱类型中几乎没有什么约束。
强类型语言中不允许有任意的隐式类型转换,而弱类型语言中则允许任意的数据隐式类型转换。
强类型不允许随意的隐式类型转换,而弱类型则是允许的。
变量类型允许随时改变的特点,不是强弱类型的差异。
JavaScript就是动态型语言,而且变量的类型随时可以改变。
常用编程语言:
JavaScript是弱类型且动态类型,甚至可以说它没有类型。它的特征就是[任性]、[不靠谱],缺失了类型系统的可靠性。
JavaScript没有编译环节
大规模应用下,弱类型/动态类型这种优势就变成了短板。
弱类型的问题:君子约定有隐患,强制要求有保障。
强类型的优势:
//@flow function sum(a: number, b: number) { return a + b } console.log(sum(10, 20)); console.log(sum('10', '20'));
Flow只是一个小工具。
安装:flow-bin
通过编译除注解
flow开发者工具:flow language support
类型注解
function add(a:number,b:number){ return a+b } let num:number = 90 function foo():void{}
flow原始类型
const a: string = 'foobar' const b: number = Infinity // NaN // 100 const c: boolean = false // true const d: null = null const e: void = undefined const f: symbol = Symbol()
flow数组类型
const arr1: Array<number> = [1, 2, 3] const arr2: number[] = [1, 2, 3] // 元组 const foo: [string, number] = ['foo', 100]
flow对象类型
const obj1: { foo: string, bar: number } = { foo: 'string', bar: 100 } const obj2: { foo?: string, bar: number } = { bar: 100 } const obj3: { [string]: string } = {} obj3.key1 = 'value1' obj3.key2 = 'value2'
flow函数类型
//@flow function foo (callback: (string, number) => void) { callback('string', 100) } foo(function (str, n) { // str => string // n => number })
flow特殊类型
// 字面量类型 const a: 'foo' = 'foo' const type: 'success' | 'warning' | 'danger' = 'success' // ------------------------ // 声明类型 type StringOrNumber = string | number const b: StringOrNumber = 'string' // 100 // ------------------------ // Maybe 类型 const gender: ?number = undefined
任意类型 Mixedany
//@flow // string | number | boolean | .... function passMixed (value: mixed) { if (typeof value === 'string') { value.substr(1) } if (typeof value === 'number') { value * value } } passMixed('string') passMixed(100) // --------------------------------- function passAny (value: any) { value.substr(1) value * value } passAny('string') passAny(100)
任何一种JavaScript运行环境都支持。功能更为强大,生态也更健全、更完善。
Typescript是JavaScript的超集。
微软自研的开发工具对Typescript支持特别好。
Typescript(渐进式)–前端领域中的第二语言。
Typescript缺点:
使用Typescript之前要先安装依赖。
标准库就是内置对象所对应的声明。
显示中文错误信息:yarn tsc --locale zh-CN,vs-code也可以设置locale为中文
我们可以用立即执行函数/export导出模块来创建一个单独作用域。
//立即执行函数 (function () { const a = 9 })() //模块导出 export aa = { a: 23 }
//原始数据类型 const a: string = 'foo' const b: number = 100 //NaN/Infinity const c: boolean = true //false const d: boolean = null const e: void = undefined const f: null = null const g: undefined = undefined const h: symbol = Symbol()
并不单指普通的对象类型,而是泛指所有的非原始类型:对象、数组、函数。
//Object类型 const aa: object = function () { } //[]//{} const obj: { foo: number, bar: string } = { foo: 123, bar: 'aaa' }
更专业的方式是使用接口。
有两种定义方式:
//数组类型 const arr: Array<number> = [1, 2, 3] const arr1: number[] = [1, 2, 3]
是一种特殊的数据结构,其实元组就是一个明确元素数量以及每个元素类型的数组,各个元素的类型不必要完全相同。定义方式:字面量方式
//元组类型 //元组(tuple) export { } //确保跟其他示例没有成员冲突 const tuple: [number, string] = [10, 'rock'] console.log(tuple[0]); //10 console.log(tuple[1]); //rock //解构赋值 const [num, age] = tuple Object.entries({ foo: 123, zar: 432 })
enum Status { Draft = 'aaa', Unpulished = 'bbb', Published = 'ccc' }
如果确认代码中不会使用索引器去访问枚举,就可以使用常量枚举。
//常量枚举 const post = { title: 'Hello', content: 'TypeScript', status: 'ok' }
枚举类型会入侵到我们运行时的代码,它会影响到我们编译后的结果。我们在TypeScript中使用的大多数类型,它在经过编译转换后,最终都会被移除掉,因为它只是为了我们在编译过程中做类型检查,但枚举类型不会,它最终会变为一个双向键值对对象。
JavaScript中又两种函数定义方式:
函数声明
//函数声明方式 // function func1(a: number, b?: number): string { // function func1(a: number, b: number=90): string { function func1(a: number, ...rest: number[]): string { return 'hello' } func1(12, 34) func1(30)
使用参数可选、参数默认值、剩余参数都需要放在参数列表的最后一个参数位置。
函数表达式
//函数表达式 const func2 = (a: number, b: number) => string = function (a: number, b: number): string { return 'func2' }
因为any是弱类型,也是动态类型,所以TypeScript不会对any做类型检查。所以它存在类型安全问题,我们不要轻易去使用它。
function stringify(value: any) { return JSON.stringify(value) } stringify('string') stringify(123) stringify(true) let foo: any = 'string' foo = 100 foo.bar()
如果我们没有通过类型注解去标注一个变量,TypeScript会根据这个变量的使用情况去推断这个变量的类型。
//隐式类型推断 let age = 10 //number // age = 'aaa' let foo; foo = 45 foo = 'aaa'
虽然定义变量时如果不给它指定类型,也不给初始值,TypeScript会自动帮他注解为any类型,但还是建议定义时就注明类型。
在某些特殊情况下,TypeScript无法去推断一个变量的类型,而我们作为开发者,我们根据代码的使用情况,我们是会明确知道这个变量到底是什么类型的。类型断言的方式:
const nums = [12, 34, 56] const res = nums.find(i => i > 10) //断言方式一--as关键字 const num1 = res as number //断言方式二---泛型 const num2 = <number>res
类型断言并不是类型转换,类型转换是代码在运行时的概念,而类型断言是代码在编译时的概念。当代码编译过后,断言也就不存在了。
一种规范或者一种契约。它可以用来去约定对象的结构。去使用一个接口,就必须去遵守它所有的约定。
interface Post { title: string content: string } function printPost(post: Post) { console.log(post.title); console.log(post.content); } printPost({ title: 'hello', content: 'welcome' }) //hello //welcome
TypeScript中的接口只是为了我们有结构的数据,去做类型约束的,在实际运行阶段,它并没有任何意义。
interface Post { title: string subtitle?: string //可选成员 content: string readonly summary: string //只读成员 }
类的特征:描述一类具体事物的抽象特征。
类可以用来描述一类具体对象的抽象成员。
ES6以前,JavaScript都是通过函数+原型模拟实现类。ES6开始,JavaScript中有了专门的class。
TypeScript增强了class的相关语法。
class Person { name: string age: number constructor(name: string, age: number) { this.name = name this.age = age } sayHi(msg: string): void { console.log((`I am ${this.name}, ${msg}`)); } }
类的访问修饰符(默认是public修饰符)
class Person { public name: string // = 'init name' private age: number protected gender: boolean constructor (name: string, age: number) { this.name = name this.age = age this.gender = true } sayHi (msg: string): void { console.log(`I am ${this.name}, ${msg}`) console.log(this.age) } } class Student extends Person { private constructor (name: string, age: number) { super(name, age) console.log(this.gender) } static create (name: string, age: number) { return new Student(name, age) } } const tom = new Person('tom', 18) console.log(tom.name) // console.log(tom.age) // console.log(tom.gender) const jack = Student.create('jack', 18)
类的只读属性readonly
protected readonly gender:boolean
类与接口
接口就是把共同的特征封装起来。
interface EatAndRun { eat(food: string): void run(distance: number): void } class Person implements EatAndRun { eat(food: string): void { console.log(`美美的吃:${food}`); } run(distance: number) { console.log(`直立行走:${distance}`); } } class Animal implements EatAndRun { eat(food: string): void { console.log(`美美的吃:${food}`); } run(distance: number) { console.log(`直立行走:${distance}`); } }
一个接口只约束一个能力,一个类型去实现多个接口。
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}`); } }
抽象类与接口有点类似,也可以用来去约束子类中必须拥有某个成员。
但是抽象类可以包含一些具体的实现,但是接口只是成员的抽象,不包含具体的实现。
抽象类不能用new去实例了,只能去继承。
abstract class Animal { eat(food: string): void { console.log(`咕噜咕噜吃:${food}`); } abstract run(distance: number): void } class Dog extends Animal { run(distance: number): void { console.log(`四肢爬行:${distance}`); } } const d = new Dog() d.eat('肉') d.run(200)
泛型:我们在定义函数、接口或类时,没有指定具体的类型,等到使用时再去指定具体类型的特征。
function CreateNumberArray(length: number, value: number): number[] { const arr = Array<number>(length).fill(value) return arr } function CreateStringArray(length: number, value: string): string[] { const arr = Array<string>(length).fill(value) return arr } function CreateArray<T>(length: number, value: T): T[] { const arr = Array<T>(length).fill(value) return arr } const res = CreateArray<string>(3, 'foo')
一个成员在定义时没有声明类型,在使用时单独为它做出明确声明。
import { camelCase } from 'lodash' import qs from 'query-string' qs.parse('?key=value&key2=value2') declare function camelCase(input: string): string const res = camelCase('hello')