Typescript
从本文你能理解到那些常识
- 强类型和弱类型
- 动态类型和动静类型
- JavaScript 自有类型零碎的问题
- Flow 动态类型查看计划
- TypeScript 语言标准和根本应用
强类型与弱类型(类型平安)
TypeScript 是一门基于 JavaScript 之上的编程语言,它重点解决了 JavaScript 语言自身类型零碎有余的问题。
强类型:具备类型束缚 不容许隐式类型转换 束缚后,不合乎类型的编译阶段就会报错,而不是执行阶段
弱类型:不具备类型束缚 容许隐式类型转换
动态类型和动静类型
动态类型 一个变量申明时他的类型就是明确的,并且不可批改
动静类型 运行阶段才能够明确变量类型,而且变量的类型随时能够扭转
js 弱类型产生的问题
const obj ={}
obj.foo() // 只有运行阶段才发现错误
// 如果谬误放异步中,就会存有隐患
setTimeout(() => {obj.foo()
}, 1000000);
// 类型不明确导致无奈确定函数性能
function sum (a,b){return a+b}
console.log(sum(1,2));
console.log(sum("100",100));
相比于弱类型,强类型的劣势
- 谬误更早的裸露
- 代码更智能,编码更精确 依据强类型的智能提醒
- 重构更可靠
- 缩小不必要的判断
Flow JS 类型查看器
2014 facebook
类型注解可控不是都必须加
-
flow 装置
npm install flow-bin -D npx flow init // 生成.flowconfig
应用 flow 时敞开 vscode 的语法校验,因为增加的类型注解不是规范的 js 语法
文件 -> 首选项 -> 设置 -> javascript validate
增加正文
@flow
,标记进行 flow 查看的文件
控制台应用 yarn flow
会查找 node_modules 里的 .bin/flow
进行类型查看
//@flow
function sum(n:number,m:number){return n+m}
// console.log(sum("100",100)) //"100" 会报错
console.log(sum(100,200))
yarn flow stop
完结服务
Flow 移除注解
@flow 并不是 js 的语法。因而执行 node + 文件.js 或浏览器中 会在控制台报错
编码过后主动移除掉类型注解
-
形式 1 flow 官网移除
- 装置:
yarn add flow-remove-types --dev
- 应用:
yarn flow-remove-types . -d dist
点(改成 src)是所有文件 前面是输入地位,作用是去除类型注解,在 node 和浏览器中应用 - 会在 dist 生成对应 js 文件
- 装置:
-
形式 2 babel 插件
-
装置:
npm i @babel/core @babel/cli @babel/preset-flow -D
yarn add @babel/core @babel/cli @babel/preset-flow --save
- 应用:
yarn babel src/part2_typescript/ts_typescript_06.js -d dist/js
yarn babel src -d dist
编译 src 文件用 babelflow
-
Flow vscode 插件
- Flow Language Support,益处是在编译阶段就报错下面都是运行后控制台报错
Flow 类型推断
-
倡议还是增加类型注解
function square(n){return n * n} // square("100") // n 报错 let num:number = 1 // num = "streing"
Flow 类型注解类型
string number boolean null undefined symbol
array object function
const a:string = "1"
const b:number = NaN//100, Infinity
const c:boolean = true
const d:null = null
const e:void = undefined
const f:symbol = Symbol()
const arr:Array<any> = [1,true]
const arr1:any[] = [1,true]
const fw:[string,number] = ['foow',1] // 固定长度类型的数组成为元组
const obj_:{foo?:string,bar:number} = {foo:"1as",bar:1} // 能够可选
const obj:{[string]:string} = {} // 通过 key 给空对象增加,约定 key 和 value
ob.key1 = "value"
function fn(callback:(string,number)=>void){ // 函数参数指定函数
callback("str",1)
}
fn(function(str,n){})
非凡类型
// 字面量
const wc:"foo" = "foo"
// 字面量配合联结类型
const type:'success' | "warning" | "danger" = "success"
const nb: string | number = 1
// 类型别名
type StringOrNumber = string | number
const sb:StringOrNumber = "stri"
//MayBe 类型,在具体类型根底上扩大了 null 和 undefined 两个值, 增加?
const gender:?number = null
const gender1:number | null | void = undefined // 同上
//Mixed Any 任意类型 所有类型的联结类型 any 是弱类型,mixed 是强类型
//string | number | boolean
function passMix(value:mixed){
// value * value 报错
// value.split(",") 报错
if(typeof value === 'string'){ // 这个联结类型须要判断
value.split(",")
}
if(typeof value === 'number'){value * value}
}
function passAny(value:any){
value * value
value.split(",")
}
typescript
ts 官网
-
装置
npm i typscript -D npm i ts-node -D tsc --init
tsc xxx
ts-node xxx
相比拟 flow 生态健全弱小,欠缺 且渐进式的,什么不会也齐全当作 js 用
毛病是多了很多概念,接口泛型枚举
不同文件同名抵触的问题。js 不会报错会笼罩,然而 ts 会在显示阶段就揭示你报错,
因而,通过 IIFE
或者 export{}
模式将文件造成公有作用域
// 独自作用域
// (function(){const hello = (name:string) =>console.log(`Hello,${name}`);
hello("TypeScript")
// 原始类型
//string,number,boolean 都能够设置成 null or undefined
const a:string = "aa"
const b:number=1//NaN Infinity
const c:boolean= false
const d:string = null
const e:void = undefined //null
const f:null = null
const g:undefined = undefined
const h:symbol = Symbol()
//})()
// 改成模块,所有成员都是部分成员,不是导出了个对象
export {}
首选项 typescript local -> zh-CN
批改成中文谬误提醒
object 类型
//Object 类型不单指对象,而是原始类型以外的其余类型,
//`number,string,boolean,symbol,null 或 undefined` 之外的类型
const foo:object = function (){}//{},[]
const obj:{foo:number,bar:string} = {foo:123,bar:"as"}// 这种模式能够用 interface 搞定
declare function create(o: object | null): void;
create({prop: 0}); // OK
create(null); // OK
create(42); // Error
create("string"); // Error
create(false); // Error
create(undefined); // Error
array 类型
const arr:Array<number> = [1,2,3]
const arr2:number[] = [1,2,3]
function sum(...args:number[]){return args.reduce((executor,current)=>{return executor+current})
}
console.log(sum(1,2,3))
元组
// 明确元素数量和元素类型的数组
const tuple:[number,string] = [1,"1"]
tuple[0]
const [age,name] = tuple
枚举
const status = {
Before:0,
Doing:1,
After:2
}
// 默认从 0 开始累加,枚举会影响咱们编译后的后果(看下编译的 js 文件),
enum Status{
Before,
Doing,
After
// After = "aff" // 字符串枚举,不常见
}
// const enum Status{ // 加 const 为常量枚举
// Before,
// Doing,
// After
// }
const s = {current:Status.Doing}
console.log(s);
// 有 const 不能通过索引拜访枚举名
console.log(Status[0])
函数类型
// 函数类型
function sth(a:number,b?:number,c:number=10):string {return ""}
function sth1(a:number,...args:number[]):string {return ""}
const sth2:(a:number,b:number)=>string = function (a:number,b:number):string {return ""}
任意类型
//any 不会做类型查看,语法上不会报错
function sth3(s:any){}
隐式类型推断
//(倡议增加明确类型)let num = 18
// num = "" // 类型谬误
let fx //any 类型
类型断言
// 并不是类型转换,代码编译过后不存在了就
const nums = 1;
const mn = nums as number
const mn2 = <number>nums //jsx 不反对
接口 interface
约定一个对象有哪些成员,这些成员类型是什么
接口就是用来束缚对象的构造,一个对象实现一个接口他就必须领有接口所束缚的成员
接口只是类型束缚,运行阶段就没了
interface Person{
age:number // 逗号,分号,不加
name:string
sex?:string
readonly height:number
[key:string]:any
}
function student(s:Person) {console.log(s.age);
console.log(s.name);
}
const stud:Person = {
age:1,
name:"as",
height:199
}
// stud.height = 180
stud.va = "asd"
stud.as = 112
函数类型接口
interface SearchFunc {(source: string, subString: string): boolean;
}
let mySearch = <SearchFunc>function(source: string, subString: string) {let result = source.search(subString);
return result > -1;
}
类类型接口
interface ClockInterface {
currentTime: Date;
setTime(d: Date);
}
class Clock implements ClockInterface {
currentTime: Date;
setTime(d: Date) {this.currentTime = d;}
constructor(h: number, m: number) {}}
类动态局部与实例局部的区别
当你操作类和接口的时候,你要晓得类是具备两个类型的:动态局部的类型和实例的类型。你会留神到,当你用结构器签名去定义一个接口并试图定义一个类去实现这个接口时会失去一个谬误:
interface ClockConstructor {new (hour: number, minute: number);
}
class Clock implements ClockConstructor {
currentTime: Date;
constructor(h: number, m: number) {}}
这里因为当一个类实现了一个接口时,只对其实例局部进行类型查看。constructor 存在于类的动态局部,所以不在查看的范畴内
因而,咱们应该间接操作类的动态局部。看上面的例子,咱们定义了两个接口,ClockConstructor
为构造函数所用和 ClockInterface
为实例办法所用。为了不便咱们定义一个构造函数 createClock
,它用传入的类型创立实例。
interface ClockConstructor {new (hour: number, minute: number): ClockInterface;
}
interface ClockInterface {tick();
}
function createClock(ctor: ClockConstructor, hour: number, minute: number): ClockInterface {return new ctor(hour, minute);
}
class DigitalClock implements ClockInterface {constructor(h: number, m: number) { }
tick() {console.log("beep beep");
}
}
let digital = createClock(DigitalClock, 12, 17);
接口继承
interface Shape {color: string;}
interface PenStroke {penWidth: number;}
interface Square extends Shape, PenStroke {sideLength: number;}
let square = <Square>{};
square.color = "blue";
square.sideLength = 10;
square.penWidth = 5.0;
class 类
形容一类具体事务的形象特色 (手机)
申明,修饰符,构造函数,静态方法
class Person{
name:string // 要申明相比 js(es7)private age:number // 默认 public
protected gender:boolean
readonly sex:string = "as" // 只读
constructor(name:string,age:number){
this.name = name
this.age = age
}
say(msg:string):void{console.log(`hi ${this.name},${msg}`);
}
}
const mc = new Person("mcgee",18)
mc.name
// mc.age
// mc.gender
class Student extends Person{constructor(name:string,age:number){super(name,age)
console.log(this.gender); // 可拜访
}
}
class Student2 extends Person{private constructor(name:string,age:number){ // 构造函数增加 private 则无奈实例化,无奈被继承
super(name,age)
console.log(this.gender);
}
static create(name:string,age:number){return new Student2(name,age)
}
}
// new Student2("MC",16) // 错
Student2.create("MC",16) // 能够
// 给构造函数增加 protected 无奈被实例化,然而能够被继承
类与接口
类与类之间有公共特色,通过接口形象(手机和座机都能打电话)
interface Eat{eat (food:string):void
}
interface Run{run (distance:number):void
}
class Person implements Eat,Run{ // 类实现了接口必须有接口的成员
eat(food:string){ }
run(distance:number){}}
// class Animal implements EatAndRun{// eat(food:string){//}
// run(distance:number){//}
// }
抽象类
相似于接口,束缚子类中必须有某个成员,区别是抽象类能够蕴含一些具体的实现,接口只能是成员的形象不蕴含具体实现
抽象类只能被继承,不能 new
abstract class Animal{eat(food:string):void{console.log(food);
}
abstract run(distance:number):void // 形象办法不须要办法体
}
class Dog extends Animal{run(distance:number):void{}}
const d = new Dog()
d.eat("sss")
d.run(100)
泛型
定义函数,接口或类的时候没有指定具体类型,等到咱们应用的时候再去指定具体类型
目标是极大水平的复用代码
function arraynumber(length:number,value:number) :number[]{return Array<number>(length).fill(value)
}
const res = arraynumber(3,100) //[100,100,100]
// 如果再定一个 arraystring
function arraystring(length:number,value:string) :string[]{return Array<string>(length).fill(value)
}
// 把定义时不能明确的类型变成参数,应用时传递
function createArray<T>(length:number,value:T):T[]{return Array<T>(length).fill(value)
}
createArray<string>(4,"mcgee")
泛型类型
function identity<T>(arg: T): T {return arg;}
let myIdentity: <U>(arg: U) => U = identity;
// 泛型类型接口
interface GenericIdentityFn {<T>(arg: T): T;
}
function identity<T>(arg: T): T {return arg;}
let myIdentity: GenericIdentityFn = identity;
// 绑定了接口泛型类型
interface GenericIdentityFn<T> {(arg: T): T;
}
function identity<T>(arg: T): T {return arg;}
let myIdentity: GenericIdentityFn<number> = identity;
泛型类
class GenericNumber<T> {
zeroValue: T;
add: (x: T, y: T) => T;
}
let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) {return x + y;};
泛型束缚
// 泛型 T 满足接口 length 属性
interface Lengthwise {length: number;}
function loggingIdentity<T extends Lengthwise>(arg: T): T {console.log(arg.length); // Now we know it has a .length property, so no more error
return arg;
}
类型申明
引入第三方模块时候,如果第三方模块不蕴含类型申明,能够自申明
引入 query-string
import {camelCase} from 'lodash' //@types/lodash
import qs from 'query-string'
declare function camelCase (input:string):string // 增加后 res:string
const res = camelCase('hello camelCase') //res:any
qs.parse("?key=asdasdq122") // 自带类型束缚