菜单设计制作网站,网站建设与开发大作业,国外图片设计网站有哪些,实验室网站建设意义自学TypeScript-基础、编译、类型 TS 编译为 JS类型支持类型注解基础类型typeof 运算符高级类型class 类构造函数和实例方法继承可见性只读 类型兼容性交叉类型泛型泛型约束多个泛型泛型接口泛型类泛型工具 索引签名类型映射类型索引查询(访问)类型 类型声明文件 TypeScript 是… 自学TypeScript-基础、编译、类型 TS 编译为 JS类型支持类型注解基础类型typeof 运算符高级类型class 类构造函数和实例方法继承可见性只读 类型兼容性交叉类型泛型泛型约束多个泛型泛型接口泛型类泛型工具 索引签名类型映射类型索引查询(访问)类型 类型声明文件 TypeScript 是 JavaScript 的超集扩展了 JavaScript 的语法支持 ECMAScript 6 标准。因此现有的 JavaScript 代码可与 TypeScript 一起工作无需任何修改TypeScript 通过类型注解提供编译时的静态类型检查。TypeScript 可处理已有的 JavaScript 代码并只对其中的 TypeScript 代码进行编译将其编译成纯 JavaScript。 TS 和 JS 相比增加的功能包括
类型批注和编译时类型检查类型推断类型擦除接口枚举Mixin泛型编程名字空间元组Await
并且有些功能从 ECMA2015 反向移植过来
类模块lambda 函数的箭头语法可选参数以及默认参数
因为 TS 是 JS 的超集所以这里只研究与 JS 差别的部分
TS 编译为 JS
通常我们使用 .ts 作为 TypeScript 代码文件的扩展名。可以使用 tsc 命令将此文件编译为 JS 代码。例如
// app.ts
var message:string Hello World
console.log(message)tsc app.ts// app.ts 编译为 app.js
var message Hello World;
console.log(message);当然因为绝大多数新版本的浏览器对 ts 语法的支持可以跳过编译为 js 的步骤。
类型支持
因为 JS 类型系统存在“先天缺陷”JS 代码中绝大部分错误都是类型错误(Uncaught TypeError)增加了Bug的查找和修改时间严重影响开发效率。TS 是静态类型的编程语言可以在编译期发现错误方便Bug的查找和修改。
TS 支持所有 JS 的类型但是 JS 不会检查类型的变化但是 TS 会检查。
类型注解
在 TS 中使用类型注解来为变量添加类型约束。
let age: number 18 // 声明变量为数值类型TS 的原始类型约束关键字有
numberstringbooleannullundefinedsymbol
基础类型
数组类型严格将并不是 TS 新增的类型但是 TS 的对象约束会根据具体类型的不同进行细分使得数组也可以自成一类。
数组类型
let number: number[] [1, 3, 5]
let strings: Arraystring [a, b, c]联合类型
// 如果不确定变量的具体类型或变量可能使用多个类型则声明为联合类型
let a: number | string a // 可以声明为数值或字符串
let arr: number | string[] [a, b, c] // 可以声明为数值或字符串数组
// 如果数组中类型不止一种则这样注解
let arr: (number | string)[] [1, a, 3, b] // 数组的元素可以是数值或字符串类型别名(自定义类型) 当同一复杂类型被多次使用时可以通过类型别名简化该类型的使用
type CustomArray (number | string)[]
let arr1: CustomArray [1, a, 3, b]函数类型(参数类型、返回值类型)
// 单独指定参数和返回值的类型
function add(num1: number, num2: number): number {return num1 num2
}
const add (num1: number, num2: number): number {return num1 num2
}
// 同时指定参数、返回值类型
const add: (num1: number, num2: number) number (num1, num2) {return num1 num2
}
// 函数没有返回值则返回值类型为 void
function greet(name: string): void {console.log(Hello, name)
}
// 可选参数
function mySlice(start?: number, end?: number): void {console.log(起始索引, start, 结束索引, end)
}对象类型(即对对象的属性和方法进行类型约束)
let person: { name: string; age: number; sayHi(): void } {name: jack,age: 19,sayHi() {}
}
// 对象的属性和方法也可以是可选的
let config: { url: string; method?: string } {...}接口类型(当一个对象类型被多次使用时一般会使用接口来描述对象的类型达到复用的目的)
interface IPerson {name: stringage: numbersayHi(): void
}
let person: IPerson {name: jack,age: 19,sayHi() {}
}
// 接口类型和别名的区别在于接口只能为对象指定类型别名能为任意类型指定别名
// 如果两个接口之间有相同的属性或方法可以将公共属性或方法抽离出来通过继承来实现复用
interface Point2D { x: number; y: number }
interface Point3D extends Point2D { z: number } // 继承了 Point2D 接口元组类型(另一种类型的数组确定了元素的个数以及特定索引元素的类型)
let posision: [number, number] [39.5427, 116.2317]类型推论(在未明确指出类型的地方使用类型推论机制即省略类型注解)
// 类型推论场景1声明变量时初始化
let age 18
// 决定函数返回值时
function add(num1: number, num2: number) { return num1 num2 }类型断言 因为 ts 是静态语言在编译时对于一些较为宽泛的对象并不确定其具体的属性会造成无法访问某些特殊属性的错误。所以需要使用类型断言指定具体类型。
// 当有一个标签 a hrefhttp://www.test.com/ idlink/a
const aLink document.getElementById(link)
// 变量 aLink 的类型是 HTMLElement该是一个宽泛(不具体)的类型
// 包含所有标签公共的属性和方法例如 id 属性而不包含特有的属性例如 href
// 如要操作特有的属性或方法要进行类型断言
const aLink document.getElementById(link) as HTMLAnchorElement
// as 后的类型必须为之前对象的子类
const aLink HTMLAnchorElementdocument.getElementById(link)字面量类型 在 ts 中常量因为其值不能被改变所以其类型为特殊的定义类型即字面量类型。字面量类型可以是任意的 JS 字面量。通常字面量类型用在表示一组明确的可选值列表中。
const str Hello TS // const 声明的变量为字面量类型即 Hello TS 类型而非 string 类型
function changeDirection(direction: up | down | left | right) { // 指定类型更加清晰明确console.log(direction)
}枚举类型 枚举类型的功能类似于字面量类型联合类型的组合功能也可以表示一组明确的可选值
// 定义一组命名常量为枚举类型它描述一个值该值为这些命名常量中的一个
enum Direction { Up, Down, Left, Right }function changeDirection(direction: Direction) {return direction
}
// 类似于对象使用时可以使用 . 来访问枚举成员
changeDirection(Direction.Left)
// 枚举成员是有值的默认为从数值 0 开始自增即 Up, Down, Left, Right 值分别为 0, 1, 2, 3
// 如有需要可以在定义时初始化枚举值未明确定义初始值的以前一个初始值增加1
enum Direction { Up 10, Down, Left, Right } // 枚举值为 10, 11, 12, 13
// 也可以初始化每个枚举成员
enum Direction { Up 10, Down 16, Left 22, Right 32 }
// 枚举成员也可以定义为字符串枚举字符串枚举的每个成员必须初始化值
enum Direction { Up UP, Down DOWN, Left LEFT, Right RIGHT }any 类型(TS 不推荐使用 any 类型因为这会丢失 TS 的类型保护的优势) any 类型为任意类型会放弃 TS 的类型约束。
// 显式的声明 any 类型
let obj: any { x: 0 }
// 隐式的声明 any 类型
let obj // 不提供类型注解也不初始化赋值
function fun(param) { // 声明参数时不提供类型注解console.log(param)
}typeof 运算符
在 JS 中可以使用 typeof 运算符查看数据的类型TS 也可以使用该运算符并用于类型注解中
let p { x: 1, y: 2 }
function formatPoint(point: typeof p) {}高级类型
class 类
TS 全面支持 ES2015 中引入的 class 并添加了类型注解和其他语法(比如可见性修饰符)。class 的基础使用 TS 和 JS 相同
class Person {}
const p new Person()根据 ts 中的类型推论Person 类的实例对象 p 的类型是 Person。ts 中的 class 不仅提供了 class 语法功能也作为一种类型存在。
ts 声明类需要进行类型注解或添加默认初始值
class Person {age: numbergender 男 // 类型推论
}构造函数和实例方法
类的构造函数需要指定类型注解否则会被隐式推断为 any。构造函数不需要返回值类型
class Person {age: numbergender: stringconstructor(age: number, gender: string) {this.age agethis.gender gender}
}类的实例方法的类型注解(参数和返回值)与函数用法相同
class Point {x 10y 10scale(n: number): void {this.x * nthis.y * n}
}继承
js 提供了类的继承 extends 可以继承父类而 ts 提供另一种继承方法 implements实现接口
// extends 继承父类
class Animal {move() { console.log(animal move) }
}
class Dog extends Animal { // 继承父类bark() { console.log(wang!) }
}
const dog new Dog()// implements 实现接口
interface Singable {sing(): void
}
class Person implements Singable { // 类中必须提供接口中所有方法和属性sing() {console.log(唱歌)}
}ts 中接口和class 类的区别在于接口是对象的属性和方法的描述是一种类型、约束。而类是对象的模板类可以实例化为对象。
可见性
可以使用可见性修饰符来控制 class 的方法或属性对于 class 外的代码是否可见。可见性修饰符包括
public(公有) 公有的成员可以被任何地方访问是默认的可见性
class Animal {public move() {console.log(Moving!)}
}protected(受保护) 受保护的成员仅对其声明所在类和子类中(非实例对象)可见
class Animal {protected move() { console.log(Moving!) }
}
class Dog extends Animal {bark() {console.log(wang!)this.move() // 可以访问父类的 protected 方法}
}private(私有) 私有的成员仅对当前类可见对实例对象以及子类都是不可见的
class Animal {private move() { console.log(Moving!) }walk() {this.move()}
}只读
class 类常用的修饰符还有 readonly 修饰符用来防止在构造函数之外对属性进行赋值
class Person {readonly age: number 18constructor(age: number) {this.age age}
}类型兼容性
类型兼容性并不是一个具体的类型而是 ts 中类型的一个特性。
现在常用的两种类型系统为
Structural Type System 结构化类型系统Nominal Type System 标明类型系统
TS 采用的是结构化类型系统也叫 duck typing(鸭子类型)类型检查关注的是值所具有的形状。也就是说在结构类型系统中如果两个对象具有相同的形状则认为它们属于同一类型。
class Point { x: number; y: number }
class Point2D { x: number; y: number }
const p: Point new Point2D() // 类型注解为 Point 类型但是由于类型兼容性可以使用 Point2D 类进行实例化因为 TS 是结构化类型系统只检查 Point 和 Point2D 的结构是否相同。如果使用标明类型系统如 C#, Java 等因为是不同的类类型无法兼容。
在实际使用的对象类型中如果 y 的成员至少与 x 相同则 x 兼容 y 成员多的可以赋值给少的例如
class Point { x: number; y: number }
class Point3D { x: number; y: number; z: number }
const p: Point new Point3D()除了 class 之外TS 的其他类型也存在相互兼容的情况
接口之间的兼容性类似于 class且 class 和 interface 之间也可以兼容函数之间也有类型兼容性不过较为赋值需要考虑参数个数、参数类型、返回值类型
// 参数个数的影响参数多的兼容参数少的(参数少的可以赋值给多的)
type F1 (a: number) void
type F2 (a: number, b: number) void
let f1: F1
let f2: F2 f1
// 最常用的是数组的 forEach 方法此方法的函数参数应该有3个参数但实际使用中经常只使用第一个参数
// 即省略用不到的函数参数
arr.forEach(item {})
arr.forEach((value, index, array) {})// 参数类型的影响相同位置的参数类型要相同(原始类型)或兼容(对象类型)
type F1 (a: number) string
type F2 (a: number) string
let f1: F1
let f2: F2 f1
// 如果函数参数是接口或 class则和之前的接口或对象兼容性冲突
interface Point2D { x: number; y: number }
interface Point3D { x: number: y: number; z: number }
type F2 (p: Point2D) void
type F3 (p: Point3D) void
let f2: F2
let f3: F3 f2 // f2 的成员少则可以赋值给成员多的 f3不可以反过来// 返回值类型的影响只关注返回值类型本身
// 返回值类型是原始类型可以互相兼容
type F5 () string
type F6 () string
let f5: F5
let f6: F6 f5
// 返回值类型是对象或接口成员多的可以赋值给成员少的
type F7 () { name: string }
type F8 () { name: string; age: number }
let f7: F7
let f8: F8
f7 f8交叉类型
交叉类型()功能类似于接口继承(extends)用于组合多个类型为一个类型(常用于对象类型)
interface Person { name: string }
interface Contact { phone: string }
type PersonDetail Person Contact // PersonDetail 同时具备了 Person 和 Contact 的所有属性类型和 extends 的不同在于同名属性之间处理类型冲突的方式不同
interface A {fn: (value: number) string
}
interface B extends A {fn: (value: string) string // 会报错因为 extends 检查到类型不兼容
}
interface C {fn: (value: string) string
}
type D A C // 不会报错交叉类型兼容两者相当于 fn: (value: string | number) string泛型
泛型是在保证类型安全的前提下让函数等与多种类型一起工作从而实现复用常用于函数、接口、class 中。例如
// 函数返回参数数据本身可以接收任意类型。如果不使用 any 类型普通的类型只能实现单一类型而使用泛型则可以实现
// 在函数名称后使用 尖括号并添加类型变量 Type。它是一种特殊类型的变量它处理类型而不是值
function idType(value: Type): Type { return value }
// 调用时再指定 Type 的具体类型
const num idnumber(10)
const str idstring(a)
// 在实际使用中通常会省略尖括号达到简化使用
const num1 id(10)
const str1 id(a)泛型约束
因为默认情况下泛型函数的变量类型 Type 可以代表多个类型这导致无法访问任何属性例如
function idType(value: Type): Type {console.log(value.length) // 会报错因为类型 Type 不一定有 length 属性return value
}此时可以添加泛型约束来收缩类型范围。泛型约束主要有两种方式1.指定更加具体的类型2.添加约束
// 指定更加具体的类型收缩至数组类型
function idType(value: Type[]): Type[] {console.log(value.length)return value
}// 添加约束满足某些条件要求
interface ILength { length: number }
function idType extends ILenght(value Type): Type {console.log(value.length)return value
}多个泛型
泛型的类型变量可以有多个且类型变量之间也可以进行约束。例如
// 设置两个泛型第二个泛型受到第一个泛型约束
function getPropType, Key extends keyof Type(obj: Type, key: key) {return obj[key]
}泛型接口
接口也可以使用泛型不过在使用时需要显式指定具体类型。
interface IdFuncType {id: (value: Type) Typeids: () Type[]
}
let obj: IdFuncnumber {id(value) { return value },ids() { return [1, 3, 5] }
}实际上 JS 中的数组在 TS 中就是一个泛型接口
const strs [a, b, c]
const nums [1, 2, 3]
// forEach 方法的参数就是根据数组元素不同而不同泛型类
class 也可以使用泛型例如 React 的组件的基类就是泛型类不同的组件会有不同的成员类
interface IState { count: number }
interface IProps { maxLength: number }
class InputCount extends React.ComponentIProps, IState {state: IState { count: 0 }render() { return div{this.props.maxLength}/div }
}创建泛型类类似于创建泛型接口
class GenericNumberNumType {defaultValue: NumTypeadd: (x: NumType, y: NumType) NumType
}
const myNum new GenericNumbernumber()
myNum.defaultValue 10泛型工具
TS 内置了一些常用的工具类型来简化 TS 中的一些常见操作最常用的有以下几种
PartialType 用来构建一个类型将 Type 的所有属性设置为可选
interface Props {id: stringchildren: number[]
}
type PartialProps PartialProps // Props 的属性是必选的经过 Partial 处理新类型所有属性为可选ReadonlyType 用来构建一个类型将 Type 的所有属性设置为只读PickType, Keys 可以从Type中选择一组属性来构造新类型
interface Props {id: stringtitle: stringchildren: number[]
}
type PickProps PickProps, id | titleRecordKeys, Type 用来构造一个对象类型属性键为Keys属性类型为Type注意所有属性类型是相同的
// 创建一个对象属性键为 a, b, c 类型均为 string[]
type RecordObj Recorda | b | c, string[]
// 等同于
type RecordObj {a: string[];b: string[];c: string[];
}索引签名类型
绝大多数情况下在使用对象前就能确定对象的结构并为对象添加准确的类型。但是偶尔无法确定对象中有哪些属性(或者说对象中可以出现任意多个属性)此时可以使用索引签名类型。
interface AnyObj {[key: string]: number
}该例中使用 [key: string] 来约束该接口中允许出现的属性名称类型这样可以出现任意多个符合约束的属性。 key 只是一个占位符可以换成任意合法变量名称。例如 JS 中对象 {} 的键是 string 类型的。
映射类型
映射类型可以基于旧类型创建新类型(对象类型)减少重复提升开发效率。例如之前的泛型类型在内部是由映射类型实现的。
// 根据联合类型创建
type PropKeys x | y | z
type Type1 { x: number; y: number; z: number}
// 使用映射类型实现
type Type2 { [key in PropKeys]: number }// 根据对象类型创建
type Props { a: number; b: string; c: boolean }
type Type3 { [key in keyof Props]: number }// 泛型工具类型 PartialType 的实现
type PartialType {[P in keyof Type]?: T[P]
}映射类型是基于索引签名类型的所以该语法类似于索引签名类型也使用 []。需注意的是映射类型只能在类型别名中使用不能在接口中使用。
索引查询(访问)类型
在 PartialType 的实现中 T[P] 的语法在 TS 中叫做索引查询(访问)类型可以用来查询属性的类型
type Props { a: number; b: string; c: boolean }
type TypeA Props[a] // 即 number
type TypeB Props[a | b] // number | string
type TypeC Props[keyof Props] // number | string | boolean类型声明文件
TS 需要编译成 JS 代码执行TS 提供了类型保护机制为了将此机制延续到 JS可以使用类型声明文件来为 JS 提供类型信息。
TS 中有两种文件类型ts 文件和 d.ts 文件。ts 文件就是 ts 的可执行代码文件 d.ts 文件即类型声明文件只做类型声明使用。
使用类型声明文件在 d.ts 文件中使用 export 导出(也可以使用import/export实现模块化功能)。在需要使用共享类型的 ts 文件中通过 import 导入即可(导入时 .d.ts 后缀可以省略)
// index.d.ts
type Props { x: number; y: number }export { Props }// a.ts
import { Props } from ./indexlet p1: Props { x: 1, y:2 }