TypeScript 是 JavaScript 的一个超集,支持 ECMAScript 6 标准。
TypeScript 由微软开发的自由和开源的编程语言。
TypeScript 设计目标是开发大型应用,它可以编译成纯 JavaScript,编译出来的 JavaScript 可以运行在任何浏览器上。
以下功能是从 ECMA 2015 反向移植而来:
TypeScript 是 JavaScript 的超集,扩展了 JavaScript 的语法,因此现有的 JavaScript 代码可与 TypeScript 一起工作无需任何修改,TypeScript 通过类型注解提供编译时的静态类型检查。
TypeScript 可处理已有的 JavaScript 代码,并只对其中的 TypeScript 代码进行编译。
let 变量:类型;
let 变量:类型 = 值
function fn(参数:类型, 参数:类型):类型{...}
类型 | 例子 | 描述 |
number | 1, -33, 2.5 | 任意数字 |
string | ‘hi’, “hi”, hi | 任意字符串 |
boolen | true, false | 布尔值true或false |
字面量 | 其本身 | 限制变量的值就是该字面量的值,定义赋值之后就不能再改变 |
any | * | 任意类型,相当于对该变量关闭了TS的类型检测,可以直接赋值给其他变量 |
unknown | * | 类型安全的any,不能直接赋值给其他变量 |
void | 空值(undefined) | 没有值(或undefined) |
never | 没有值 | 不能是任何值 |
object | {key:value} | 任意的js对象 |
array | [1, 2, 3] | 任意的js数组 |
tuple元组 | [4, 5] | 元素,TS新增的类型,固定长度的数组 |
enum | enum{A, B} | 枚举,TS中新增的类型 |
额外知识点:
类型断言——可以用来告诉解析器变量的实际类型
语法
变量 as 类型
<类型> 变量
TS目前不能在浏览器上直接运行,需要转化为响应的js文件。则通过 tsc xxx.ts 命令进行编译,但是我们不可能每改一次代码执行一次这个命令,所以需要对文件进行监听。
//命令行代码:
tsc xxx.ts -w // w watch的缩写
但是前面这种写法只能监听一个TS文件,监听多个文件只能开启多个终端,这不符合我们的日常开发需求。所以我们真正开发时采用以下的方法。
tsconfig.json
{
//用来指定哪些ts文件要被编译
"include":[
'./scr/**/*', //**表示任意目录, *表示任意文件
]
}
tsconfig.json
{
"exclude":[
"./src/banner/**/*"
]
}
tsconfig.json
{
"extends":’./config/base‘ //继承config/base.json文件
}
官网案例
{
"files": [
"core.ts",
"sys.ts",
"types.ts",
"scanner.ts",
"parser.ts",
"utilities.ts",
"binder.ts",
"checker.ts",
"emitter.ts",
"program.ts",
"commandLineParser.ts",
"tsc.ts",
"diagnosticInformationMap.generated.ts"
]
}
东西太多 无法一一列举 详情需访问官网 或者初始化配置 tsc --init 来查看
'compilerOptions':{
"target" :"ES5", //用来设置编译的ECMAscript版本+
"module":"es6", //指定要使用的模块化规范
"lib":[ ], //lib用来指定项目中要使用的库
"outDir":"./dist:", //用来指定编译后文件所在的目录
"outFile":"./dist/app.js" //将编译的代码合并到一个文件上
"allowJs":true, //是否对JS文件进行编译
"removeComments":true //是否移除注释
"noEmit":false //不生成编译后的文件
"noEmitOnError":true //当有错误时不编译文件
"strict":true //所有严格检查的总开关 这个开启 底下的四个默认为true 推荐为true
"alwaysStrict":false //设置是否为编译后的文件开启严格模式
"noImpLicitAny":true, //不允许隐式的any类型(就是必须声明)
"noImplicitThis":true, //不允许不明确类型的this 方法中的this需指定在参数中 this:any
"strictNullChecks":true //严格检查空值 如果有可能存在null则报错
}
npm init -y //生成package.json文件
npm install -D webpack webpack-cli typescript ts-loader
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack"
},
//引入node 路径包
const path = require('path')
//导出所有webpack配置
module.exports = {
//指定路口文件
entry: "./src/index.ts",
//指定打包文件所在目录
output: {
//指定打包文件的目录
path: path.resolve(__dirname, 'dist'),
//打包的文件名
filename: "bundle.js",
},
//指定webpack打包时使用的模块
module: {
//指定要加载的规则
rules: [
{ //指定规则生效的文件
test: /\.ts$/,
//使用ts-loader打包ts文件
use: "ts-loader",
//设置不需要打包的文件
exclude: /node_modules/
}
]
}
}
{
//这是自己配置
"compilerOptions": {
"module": "ES2015",
"target": "ES2015",
"strict": true
}
}
npm i -D html-webpack-plugin
之后需在webpack.config.js中配置
const htmlWebpackPlugin = require('html-webpack-plugin')
//导出所有webpack配置
module.exports = {
//配置webpack插件
plugins: [
new htmlWebpackPlugin()
]
}
module.exports = {
//用来设置引用模块格式
resolve: {
extensions: ['.ts', '.js'] //表示允许.ts 和.js文件进行模块化操作(例如模块的引入 导出)
}
}
npm i -D @babel/core @babel/preset-env babel-loader core.js
安装完之后需要在webpack.config.js中进行babel的配置:
module.exports = {
//告诉webpack不使用箭头函数
//因为webpack打包时会将代码块打包到一个自调用函数中,该函数内部使用箭头函数
//而IE不支持
evironment:{
arrowFunction:false
},
//指定webpack打包时使用的模块
module: {
//指定要加载的规则
rules: [
{ //指定规则生效的文件
test: /\.ts$/,
//使用ts-loader打包ts文件
use: [
//配置babel
{
//指定加载器
loader: "label-loader",
//设置babel
options: {
//设置预定义的环境
presets: [
[
//指定环境的插件
"@babel/preset-env",
{
//要兼容浏览器版本
targets: {
"ie": "11"
},
//指定corejs版本
"corejs": "4",
// 使用corejs的方式
"useBuiltIns": "usage" //按需加载
}
]
]
}
},
"ts-loader",
],
//设置不需要打包的文件
exclude: /node_modules/
}
]
},
}
在 TypeScript 中,我们使用接口(Interfaces)来定义对象的类型。
在面向对象语言中,接口(Interfaces)是一个很重要的概念,它是对行为的抽象,而具体如何行动需要由类(classes)去实现(implement)。 TypeScript 中的接口是一个非常灵活的概念,除了可用于对类的一部分行为进行抽象以外,也常用于对「对象的形状(Shape)」进行描述。
例子:
interface Person {
name: string
age: number
}
let tom: Person = {
name: 'Tom',
age: 25
}
上面的例子中,我们定义了一个接口 Person,接着定义了一个变量 tom,它的类型是 Person。这样,我们就约束了 tom 的形状必须和接口 Person 一致。 接口一般首字母大写。有的编程语言中会建议接口的名称加上 I 前缀。 定义的变量比接口少了一些属性是不允许的:
interface Person {
name: string
age: number
}
let tom: Person = {
name: 'Tom'
}
// Property 'age' is missing in type '{ name: string }' but required in type 'Person'.
多一些属性也是不允许的:
interface Person {
name: string
age: number
}
let tom: Person = {
name: 'Tom',
age: 25,
gender: 'male'
}
// Type '{ name: string age: number gender: string }' is not assignable to type 'Person'.
// Object literal may only specify known properties, and 'gender' does not exist in type 'Person'.
可见, 赋值的时候,变量的形状必须和接口的形状保持一致。
有时我们希望不要完全匹配一个形状,那么可以用可选属性:
interface Person {
name: string
age?: number
}
let tom: Person = {
name: 'Tom'
}
interface Person {
name: string
age?: number
}
let tom: Person = {
name: 'Tom',
age: 25
}
可选属性的含义是该属性可以存在,可以不存在。
有时候我们希望一个接口允许有任意的属性,可以使用如下方式:
interface Person {
name: string
age?: number
[propName: string]: any
}
let tom: Person = {
name: 'Tom',
gender: 'male'
}
使用 [propName: string] 定义了任意属性取 string 类型的值。 需要注意的是,一旦定义了任意属性,那么确定属性和可选属性的类型都必须是它的类型的子集:
interface Person {
name: string
age?: number
[propName: string]: string
}
let tom: Person = {
name: 'Tom',
age: 25,
gender: 'male'
}
// Property 'age' of type 'number | undefined' is not assignable to string index type 'string'.
// Type '{ name: string age: number gender: string }' is not assignable to type 'Person'.
// Property 'age' is incompatible with index signature.
// Type 'number' is not assignable to type 'string'.
上例中,任意属性的值允许是 string,但是可选属性 age 的值却是 number,number 不是 string 的子属性,所以报错了。
另外,在报错信息中可以看出,此时 { name: ‘Tom’, age: 25, gender: ‘male’ } 的类型被推断成了 { [x: string]: string | number name: string age: number gender: string },这是联合类型和接口的结合。
有时候我们希望对象中的一些字段只能在创建的时候被赋值,那么可以用 readonly 定义只读属性:
interface Person {
readonly id: number
name: string
age?: number
[propName: string]: any
}
let tom: Person = {
id: 89757,
name: 'Tom',
gender: 'male'
}
tom.id = 9527
// Cannot assign to 'id' because it is a read-only property.
上例中,使用 readonly 定义的属性 id 初始化后,又被赋值了,所以报错了。
传统方法中,JavaScript 通过构造函数实现类的概念,通过原型链实现继承。而在 ES6 中,我们终于迎来了 class。
TypeScript 除了实现了所有 ES6 中的类的功能以外,还添加了一些新的用法。
这里对类相关的概念做一个简单的介绍。
class Animal {
constructor(public name) {
this.name = name
}
sayHi() {
return `My name is ${this.name}`
}
}
let a = new Animal('Jack')
console.log(a.sayHi()) // My name is Jack
class Cat extends Animal {
constructor(name) {
super(name) // 调用父类的 constructor(name)
console.log(this.name)
}
sayHi() {
return 'Meow, ' + super.sayHi() // 调用父类的 sayHi()
}
}
let c = new Cat('Tom') // Tom
console.log(c.sayHi()) // Meow, My name is Tom
使用 getter 和 setter 可以改变属性的赋值和读取行为:
class Animal {
constructor(name) {
this.name = name
}
get name() {
return 'Jack'
}
set name(value) {
console.log('setter: ' + value)
}
}
let a = new Animal('Kitty') // setter: Kitty
a.name = 'Tom' // setter: Tom
console.log(a.name) // Jack
使用 static 修饰符修饰的方法称为静态方法,它们不需要实例化,而是直接通过类来调用:
class Animal {
static isAnimal(a) {
return a instanceof Animal
}
}
let a = new Animal('Jack')
Animal.isAnimal(a) // true
a.isAnimal(a) // TypeError: a.isAnimal is not a function
ES6 中实例的属性只能通过构造函数中的 this.xxx 来定义,ES7 提案中可以直接在类里面定义:
class Animal {
name = 'Jack'
constructor() {
// ...
}
}
let a = new Animal()
console.log(a.name) // Jack
ES7 提案中,可以使用 static 定义一个静态属性:
class Animal {
static num = 42
constructor() {
// ...
}
}
console.log(Animal.num) // 42
public private 和 protected
TypeScript 可以使用三种访问修饰符(Access Modifiers),分别是 public、private 和 protected。
abstract 用于定义抽象类和其中的抽象方法。
什么是抽象类?
首先,抽象类是不允许被实例化的;其次,抽象类中的抽象方法必须被子类实现:
abstract class Animal {
public name
public constructor(name) {
this.name = name
}
public abstract sayHi()
}
let a = new Animal('Jack')
// Cannot create an instance of an abstract class.
上面的例子中,我们定义了一个类 Cat 继承了抽象类 Animal,但是没有实现抽象方法 sayHi,所以编译报错了。
下面是一个正确使用抽象类的例子:
abstract class Animal {
public name
public constructor(name) {
this.name = name
}
public abstract sayHi()
}
class Cat extends Animal {
public sayHi() {
console.log(`Meow, My name is ${this.name}`)
}
}
let cat = new Cat('Tom')
泛型就是解决 类 接口 方法的复用性、以及对不特定数据类型的支持
// 只能返回string类型的数据
function getData(value:string):string{
return value
}
// 可返回string和number类型的数据 (代码冗余)
function getData1(value:string):string{
return value
}
function getData2(value:number):number{
return value
}
// any 相当于放弃了类型检测
function getData3(value:any):any{
return value
}
// 实现传入什么类型返回什么类型
// T 表示泛型,具体什么类型是调用这个方法的时候绝定的(也可以是其它字母)
function getData4<T>(value:T):T{ // 指定了返回的类型
// 传入什么 返回什么
return value
}
getData4<number>(123) //使用
getData4<string>("xxx")
// 返回任意类型
function getData5<T>(value:T):any{
return "dd"
}
getData5<number>(123) //使用
getData5<string>("dd")
比如有个最小堆算法, 需要同时支持返回数字和字符串2种类型
只支持 number类型
class MinClass{
public list:number[] = [];
add(num:number){
this.list.push(num)
}
min():number{ // 求最小数
var minNum = this.list[0]
for(var i = 0; i < this.list.length; i++){
if(minNum > this.list[i]){
minNum = this.list[i]
}
}
return minNum
}
}
var m = new MinClass()
m.add(22)
m.add(2)
m.add(66)
console.log(m.min()) //2
可支持多类型 对不特定数据类型的支持
class MinClass<T>{
public list:T[] = [];
add(num:T):void{
this.list.push(num)
}
min():T{ // 求最小数
var minNum = this.list[0]
for(var i = 0; i < this.list.length; i++){
if(minNum > this.list[i]){
minNum = this.list[i]
}
}
return minNum
}
}
var m = new MinClass<string>() //实例化类 并且制定了类的 T代表类型
m.add("1")
m.add("12")
m.add("23")
console.log(m.min()) //1
把类当做参数的泛型类
class User{
name: string | undefined;
pwd: string | undefined
}
class MysqlDb{
add(user:user):boolean{
return true
}
}
var u = new User()
u.name = "u-name"
u.pwd = "u-123"
var sql = new MysqlDb()
sql.add(u)
泛型类
class User{
name: string | undefined;
pwd: string | undefined
}
class MysqlDb<T>{
add(user:T):boolean{
return true
}
}
var u = new User()
u.name = "u-name"
u.pwd = "u-123"
var sql = new MysqlDb<User>()
sql.add(u)
参数传对象
class User{
name: string | undefined;
pwd: string | undefined;
constructor(params:{
name: string | undefined,
pwd: string | undefined
}){
this.name = params.name
this.pwd = params.pwd
}
}
class MysqlDb<T>{
add(user:T):boolean{
return true
}
}
var u = new User({
name:"u-name",
pwd:"u-123"
})
var sql = new MysqlDb<User>()
sql.add(u)
函数接口
interface ConFigFu{ // 函数类型接口
(value1:string,value2:string):string
}
var setData:ConFigFu = function(value1:string,value2:string):string{
return value1 + value2
}
setData("xx","yy")
泛型接口
第一种写法
interface ConFigFu{
<T>(value1:T,value2:T):T
}
var setData:ConFigFu = function<T>(value1:T,value2:T):T{
return value1
}
setData<string>("xx","yy")
第二种写法
interface ConFigFu<T>{
(value1:T,value2:T):T
}
var setData = function<T>(value1:T,value2:T):T{
return value1
}
var getdata:ConFigFu<string> = setData
getdata("xx","yy")