分章节阅读目录链接:typeScript系列学习文章目录
布尔类型(boolean)
数字类型(number)
字符串类型(string)
数组类型(array)
元组类型(tuple)
枚举类型(enum)
任意类型(any)
null 和 undefined
void类型
never类型
字符串类型(string)
var str:string = "fur"
str = "furfur"
console.log(str)//furfur
数组类型(array)
//写法一
var arr:number[] = [1,2,3]
console.log(arr)//[ 1, 2, 3 ]
//写法二
var arr:Array<number> = [1,2,3]
console.log(arr)//[ 1, 2, 3 ]
**元组类型(tuple)**属于数组的一种。
var arr:[string,number] = ["fur",1]
console.log(arr)//[ "fur", 1 ]
枚举类型(enum)
枚举方法:事先考虑到某一变量可能取的值,尽量用自然语言中含义清楚的单词来表示它的每一个值,用这种方法定义的类型称枚举类型。(就是起一个通俗易懂的别名)
enum 枚举名{
标识符[=整型常数],
标识符[=整型常数],
...
标识符[=整型常数],
} ;
enum Flag{
success = 1,
error = 0,
}
let s:Flag = Flag.success
console.log(s)//1
任意类型(any):相当于没有类型限制
var num:any=123;
num='str';
num=true;
console.log(num)
null 和 undefined是其他(never类型)数据类型的子类型
var num1:number
var num2:undefined
var num3:null=null
console.log(num1)//编译器报错
console.log(num2)//undefined
console.log(num3)//null
void类型:表示没有任何类型,一般用于定义方法的时候方法没有返回值。
function play():void{
console.log("fur")
}
play()
never类型:表示的是那些永不存在的值的类型。
never类型是任何类型的子类型,也可以赋值给任何类型;然而,没有类型是never的子类型或可以赋值给never类型(除了never本身之外)。 即使 any也不可以赋值给never。通常表现为抛出异常或无法执行到终止点(例如无线循环)。
let x: never;
let y: number;
// 编译错误,数字类型不能转为 never 类型
x = 123;
// 运行正确,never 类型可以赋值给 never类型
x = (()=>{ throw new Error('exception')})();
// 运行正确,never 类型可以赋值给 数字类型
y = (()=>{ throw new Error('exception')})();
// 返回值为 never 的函数可以是抛出异常的情况
function error(message: string): never {
throw new Error(message);
}
// 返回值为 never 的函数可以是无限循环这种无法被执行到的终止点的情况
function loop(): never {
while (true) {}
}
上述never部分的理解参考自文章:https://segmentfault.com/a/1190000008893626
联合类型
联合类型(Union Types)可以通过管道(|)将变量设置多种类型,赋值时可以根据设置的类型来赋值。
var val:string|number
val = 1
console.log(typeof(val)) //number
val = "fur"
console.log(typeof(val)) //string
函数的定义
可选参数,默认参数,剩余参数
函数重载
箭头函数 es6
函数就是包裹在花括号中的代码块,前面使用了关键词 function:
语法格式如下所示:
function run():void{
// 执行代码
}
var run = function ():void{
// 执行代码
}
方法传参
var run = function (name:string):string{
// 执行代码
return`${name}在跑步`
}
console.log(run("fur"))//fur在跑步
可选参数
es5里面方法的实参和行参可以不一样,但是ts中必须一样,如果不一样就需要配置可选参数
注意:可选参数必须配置到参数的最后面
var run = function (name:string,sex?:string):string{
// 执行代码
if(sex){
return`${name}在跑步---${sex}`
}else{
return`${name}在跑步`
}
}
console.log(run("fur"))//fur在跑步
console.log(run("fur","女"))//fur在跑步---女
默认参数
es5里面没法设置默认参数,es6和ts中都可以设置默认参数
var run = function (name:string,sex:string="女"):string{
// 执行代码
if(sex){
return`${name}在跑步---${sex}`
}else{
return`${name}在跑步`
}
}
console.log(run("fur"))//fur在跑步---女
console.log(run("fur","男"))//fur在跑步---男
剩余参数
...
三点运算符,接受新参传过来的值
function sum(...arr:number[]):number{
var item = 0;
var sum = 0;
for(item of arr){
sum+=item
}
return sum;
}
console.log(sum(1,2,3,4,5,6))//21
console.log(sum(1,2,3))//6
java中方法的重载:重载指的是两个或者两个以上同名函数,但它们的参数不一样,这时会出现函数重载的情况。
typescript中的重载:通过为同一个函数提供多个函数类型定义来试下多种功能的目的。
注意:**必须要把精确的定义放在前面,最后函数实现时,需要使用 |
操作符或者?
操作符,把所有可能的输入类型全部包含进去,以具体实现。**即最下面的方法需要兼容上面的方法
function run(num:number):void;
function run(str:string,flag:boolean):void;
function run(str:string,flag?:boolean):void;
function run(str:number|string,flag?:boolean):void{
if(typeof str === 'string'){
console.log("fur")
}else{
console.log(1)
}
}
run(1)
run("fur")
run("fur",true);
常见报错:This overload signature is not compatible with its implementation signature
意思:此重载签名与其实现签名不兼容,即没有满足最后的函数实现时,把所有可能输入的函数全部包含进去。
Lambda 函数也称之为箭头函数。箭头函数表达式的语法比函数表达式更短。
( [param1, parma2,…param n] )=>statement;
最简写法
var result = (num:number)=>num+1
console.log(result(2))//3
正常写法
var result = (num:number)=>{
//执行代码
console.log(num+1)
}
result(2)//3
类描述了所创建的对象共同的属性和方法。
类的定义
继承
类里面的修饰符
静态属性 静态方法
抽象类 继承 多态
class person {
name:string; //属性,省略public
constructor(n:string){
this.name = n
}
run():void{
console.log(this.name+"在跑步")
}
}
var p = new person("fur");
console.log(p.name)//fur
p.run()//fur在跑步
关键词:extends super
run方法说明:子类继承父类方法
study方法说明:子类重写父类方法
class person {
name:string; //属性,省略public
constructor(n:string){
this.name = n
}
run():void{
console.log(this.name+"在跑步")
}
study():void{
console.log(this.name+"在学习")
}
}
class man extends person{
constructor(name:string){
super(name) /*初始化父类的构造函数*/
}
study():void{
console.log(this.name+"在学习-子类")
}
}
var p = new person("fur");
console.log(p.name)//fur
p.run()//fur在跑步
var m = new man("furfur");
console.log(m.name)//furfur
m.run()//furfur在跑步(继承父类的run)
m.study()//furfur在学习-子类(重写父类study)
对比es5的继承有很大的改善,es5继承
类里面的修饰符 typescript里面定义属性的时候给我们提供了 三种修饰符
public : 公有,在当前类里面、 子类 、类外面都可以访问
protected:保护类型,在当前类里面、子类里面可以访问 ,在类外部没法访问
private :私有,在当前类里面可以访问,子类、类外部都没法访问
例子:
class person {
private name:string;
constructor(n:string){
this.name = n
}
protected run():void{
console.log(this.name+"在跑步")//正确,private只能在类“person”中访问
}
public study():void{
console.log(this.name+"在学习")
}
}
class man extends person{
constructor(name:string){
super(name) /*初始化父类的构造函数*/
}
protected run():void{
super.run() // 正确,protected只能在类“person”及其子类中访问。
}
}
var p = new person("fur");
// console.log(p.name)//报错:属性“name”为私有属性,只能在类“person”中访问。
// p.run()//报错:属性“run”受保护protected,只能在类“person”及其子类中访问。
p.study()//furfur在学习
var m = new man("furfur");
// console.log(m.name)//报错:属性“name”为私有属性,只能在类“person”中访问。
// m.run()//报错:属性“run”受保护protected,只能在类“man”及其子类中访问。
m.study()//furfur在学习
关键词:static
class person {
name:string;
static age = 1
constructor(n:string){
this.name = n
}
// static run():void{
// console.log(this.name+"在跑步")//静态方法 里面没法直接调用类里面的非静态属性
// }
static getAge():void{
console.log(this.age)
}
}
person.age=1
person.getAge()
多态:父类定义一个方法不去实现,让继承它的子类去实现 每一个子类有不同的表现,多态属于继承
例子:person中的paly方法,子类man和woman有各自的具体方法
class person {
name:string;
age = 1
constructor(n:string){
this.name = n
}
play():void{
console.log("玩法")
}
}
class man extends person{
constructor(name:string){
super(name)
}
play():void{
console.log(`${this.name} lol`)
}
}
class woman extends person{
constructor(name:string){
super(name)
}
play():void{
console.log(`${this.name} shopping`)
}
}
var m = new man("boy")
m.play()//boy lol
var w = new woman("girl")
w.play()//girlshopping
typescript中的抽象类:抽象类和抽象方法用来定义标准 。它是提供其他类继承的基类,不能直接被实例化。
用abstract关键字定义抽象类和抽象方法,抽象类中的抽象方法不包含具体实现并且必须在派生类中实现。abstract抽象方法只能放在抽象类里面。
抽象类的子类必须实现抽象类里面的抽象方法,否则仍然要作为一个抽象类。
abstract class person {
name:string;
age = 1
constructor(n:string){
this.name = n
}
abstract play():void;
}
class man extends person{
constructor(name:string){
super(name)
}
play():void{
console.log(`${this.name} lol`)
}
}
class woman extends person{
constructor(name:string){
super(name)
}
play():void{
console.log(`${this.name} shopping`)
}
}
var m = new man("boy")
m.play()//boy lol
var w = new woman("girl")
w.play()//girlshopping
属性类接口
函数类型接口
可索引接口
类类型接口
接口扩展
接口是一系列抽象方法的声明,是一些方法特征的集合,这些方法都应该是抽象的,需要由具体的类去实现,然后第三方就可以通过这组抽象方法调用,让具体的类执行具体的方法。
接口的作用:在面向对象的编程中,接口是一种规范的定义,它定义了行为和动作的规范,在程序设计里面,接口起到一种限制和规范的作用。
接口定义了某一批类所需要遵守的规范,接口不关心这些类的内部状态数据,也不关心这些类里方法的实现细节,它只规定这批类里必须提供某些方法,提供这些方法的类就可以满足实际需要。
typescrip中的接口类似于java,同时还增加了更灵活的接口类型,包括属性、函数、可索引和类等。
接口:行为和动作的规范,对批量方法进行约束
对json的约束
interface Person {
name:string;
age:number;
sex?:string//可选属性
}
function printPerson (p:Person){
if(p.sex){
console.log(`${p.name}年龄${p.age} 性别${p.sex}`)
}else{
console.log(`${p.name}年龄${p.age}`)
}
}
//写法一
printPerson({name:"fur",age:1})//fur年龄1
printPerson({name:"fur",age:1,sex:"girl"})//fur年龄1 性别girl
//写法二
var obj:Person = {
name:"furfur",
age:2,
sex:"girl"
}
printPerson(obj)//furfur年龄2 性别girlrfur年龄2 性别girl
对方法传入的参数 以及返回值进行约束,批量约束
interface encrypt{
(key:string,value:string):string;
}
var md5:encrypt=function(key:string,value:string):string{
//模拟操作
return key+value;
}
console.log(md5('name','fur'));//namefur
数组、对象的约束
//对数组的约束
interface Arr {
[index:number]:string;
}
var arr1:Arr = ["1","2","3"]
//对对象的约束
interface Obj {
[index:string]:string
}
var arr2:Obj = {name:"fur",age:"1"}
对类的约束和抽象类抽象有点相似
class person {
name:string;
constructor(n:string){
this.name = n
}
play():void{
}
}
class man implements person{
name: string;
constructor(name:string){
this.name = name;
}
play():void{
console.log(`${this.name} lol`)
}
}
class woman implements person{
name: string;
constructor(name:string){
this.name = name;
}
play():void{
console.log(`${this.name} shopping`)
}
}
var m = new man("boy")
m.play()//boy lol
var w = new woman("girl")
w.play()//girlshopping
接口可以继承接口
例子:接口person继承接口Animal,man实现接口person同时也要实现Animal
class Animal{
eat():void{
}
}
class person extends Animal{
play():void{
}
}
class man implements person{
eat():void {
console.log('吃粮食')
}
play():void{
console.log('玩游戏')
}
}
泛型的定义
泛型函数
泛型类
泛型接口
泛型:软件工程中,我们不仅要创建一致的定义良好的API,同时也要考虑可重用性。 组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型,这在创建大型系统时为你提供了十分灵活的功能。
在像C#和Java这样的语言中,可以使用泛型来创建可重用的组件,一个组件可以支持多种类型的数据。 这样用户就可以以自己的数据类型来使用组件。
通俗理解:泛型就是解决 类 接口 方法的复用性、以及对不特定数据类型的支持(类型校验)
泛型:可以支持不特定的数据类型, 要求:传入的参数和返回的参数一致
可以用T表示泛型,具体什么类型是调用这个方法的时候决定的
function getData<T>(val:T):T{
return val
}
console.log(getData<number>(1))//1
console.log(getData<string>("fur"))//fur
console.log(getData<boolean>(true))//true
class MinClass<T>{
public list:T[] = []
add(val:T):void{
this.list.push(val)
}
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 mNum = new MinClass<number>();
mNum.add(8);
mNum.add(6);
mNum.add(11);
console.log(mNum.min())//6
var mStr = new MinClass<string>();
mStr.add("fur")
mStr.add("kfc")
mStr.add("mdl")
console.log(mStr.min())//fur
interface config<T> {
(value:T):T
}
function getData<T>(val:T):T{
return val;
}
var myData:config<string> = getData;
console.log(myData("fur"))//fur
“外部模块”现在则简称为“模块” 模块在其自身的作用域里执行,而不是在全局作用域里;
“内部模块”现在称做“命名空间”。
这意味着定义在一个模块里的变量,函数,类等等在模块外部是不可见的,除非你明确地使用export形式之一导出它们。
相反,如果想使用其它模块导出的变量,函数,类,接口等的时候,你必须要导入它们,可以使用 import形式之一。
由于浏览器端无法解析export,可以使用node运行或者用webpack打包工具打包。
导入导出方法一
export导出:
var url='xxx';
function getData():void{
console.log('fur');
}
export {url,getData};
import导入:
import {url,getData as get } from './modules/db'
//as可以更换名字,地址是export的文件的地址
console.log(url)//xxx
get()//fur
导入导出方法二
导出方法:export default 默认导出
每个模块都可以有一个default导出。 默认导出使用 default关键字标记;并且一个模块只能够有一个default导出。 需要使用一种特殊的导入形式来导入 default导出。
export导出:
function getData():void{
console.log('fur');
}
export default getData;
import导入:
import getData from './modules/db';
getData();//fur
在代码量较大的情况下,为了避免各种变量命名相冲突,可将相似功能的函数、类、接口等放置到命名空间内,typeScript的命名空间可以将代码包裹起来,只对外暴露需要在外部访问的对象。
命名空间内的对象通过export关键字对外暴露。
命名空间和模块的区别:
命名空间:内部模块,主要用于组织代码,避免命名冲突。
模 块:ts的外部模块的简称,侧重代码的复用,一个模块里可能会有多个命名空间。
命名空间使用 namespace 来定义:
空间A和B内代码一模一样,可以共存,调用时还需要命名空间的名字调用
namespace A{
interface Person {
name:string;
paly():void;
}
export class man implements Person {
name: string;
constructor(n: string) {
this.name = n;
}
paly():void{
console.log("玩游戏")
}
}
}
namespace B{
interface Person {
name:string;
paly():void;
}
export class man implements Person {
name: string;
constructor(n: string) {
this.name = n;
}
paly():void{
console.log("跑步")
}
}
}
var p1 = new A.man("fur")
p1.paly()//玩游戏
var p2 = new B.man("fur")
p2.paly()//跑步
装饰器:装饰器是一种特殊类型的声明,它能够被附加到类声明,方法,属性或参数上,可以修改类的行为。
通俗的讲装饰器就是一个方法,可以注入到类、方法、属性参数上来扩展类、属性、方法、参数的功能。
常见的装饰器有:类装饰器、属性装饰器、方法装饰器、参数装饰器
装饰器的写法:普通装饰器(无法传参) 、 装饰器工厂(可传参)
注意:使用装饰器会报以下错误,点击快速修复或者手动在json文件里增加以下代码
"experimentalDecorators": true,
对修饰器的实验支持是一项将在将来版本中更改的功能。设置 "experimentalDecorators" 选项以删除此警告。
运行顺序:属性>方法>方法参数>类 如果有多个同样的装饰器,它会先执行后面的
类装饰器在类声明之前被声明(紧靠着类声明)。 类装饰器应用于类构造函数,可以用来监视,修改或替换类定义。
类装饰器:普通装饰器(无法传参)
function logClass(params:any){
console.log(params)
// 动态扩展属性eg
params.prototype.name = "fur"
//动态扩展方法
params.prototype.run =function(){
console.log("run")
}
}
@logClass
class Person {
study(){
console.log("study")
}
}
var p:any = new Person()
p.study()//study
console.log(p.name)//fur
p.run()//run
类装饰器:装饰器工厂(可传参)
function logClass(params:any){
return function(target:any){
target.prototype.name=params;
}
}
@logClass('furfur')
class Person {
study(){
console.log("study")
}
}
var p:any = new Person()
p.study()//study
console.log(p.name)//furfur
属性装饰器表达式会在运行时当作函数被调用,传入下列2个参数:
1、对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
2、成员的名字。
function logProperty(params:any){
return function(target:any,attr:any){
target[attr]=params;
console.log(attr)
console.log(target)
}
}
class Person {
@logProperty("fur")
name:string|undefined;
constructor(){
}
study(){
console.log("study")
}
}
var p:any = new Person()
p.study()//study
console.log(p.name)//fur
它会被应用到方法的 属性描述符上,可以用来监视,修改或者替换方法定义。
方法装饰会在运行时传入下列3个参数:
1、对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
2、成员的名字。
3、成员的属性描述符
function get(params:any){
return function(target:any,methodName:any,desc:any){
console.log(methodName);//getData
target.apiUrl='fur';
target.run=function(){
console.log('run');
}
}
}
class HttpClient{
public url:any |undefined;
constructor(){
}
@get('http://www.fur.com')
getData(){
console.log(this.url);
}
}
var http:any=new HttpClient();
console.log(http.apiUrl);//fur
http.run();//run
方法参数装饰器表达式会在运行时当作函数被调用,传入下列3个参数:
1、对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
2、参数的名字。
3、参数在函数参数列表中的索引。
function logParams(params:any){
return function(target:any,methodName:any,paramsIndex:any){
console.log(params);//fur
console.log(target);//object
console.log(methodName);//getData
console.log(paramsIndex);//0
target.apiUrl=params;
}
}
class HttpClient{
public url:any |undefined;
constructor(){
}
getData(@logParams('fur') uuid:any){
console.log(uuid);
}
}
var http:any = new HttpClient();
http.getData("furfur");//furfur
console.log( http.apiUrl);//fur
在开发过程中不可避免要引用其他第三方的 JavaScript 的库。虽然通过直接引用可以调用库的类和方法,但是却无法使用TypeScript 诸如类型检查等特性功能。
为了解决这个问题,需要将这些库里的函数和方法体去掉后只保留导出类型声明,而产生了一个描述 JavaScript 库和模块信息的声明文件。通过引用这个声明文件,就可以借用 TypeScript 的各种特性来使用库文件了。
假如我们想使用第三方库,比如 jQuery,我们通常这样获取一个 id 是 foo 的元素:
$('#foo');
// 或
jQuery('#foo');
但是在 TypeScript 中,我们并不知道 $ 或 jQuery 是什么东西:
jQuery('#foo');
// index.ts(1,1): error TS2304: Cannot find name 'jQuery'.
这时,我们需要使用 declare 关键字来定义它的类型,帮助 TypeScript 判断我们传入的参数类型对不对:
declare var jQuery: (selector: string) => any;
jQuery('#foo');
declare 定义的类型只会用于编译时的检查,编译结果中会被删除。
上例的编译结果是:
jQuery('#foo');
声明文件以 .d.ts 为后缀;
fur.d.ts
声明文件或模块的语法格式如下:
declare module Module_Name {
}
TypeScript 引入声明文件语法格式:
///
当然,很多流行的第三方库的声明文件不需要我们定义了,比如 jQuery 已经有人帮我们定义好了:jQuery in DefinitelyTyped。
以下定义一个第三方库来演示:
CalcThirdPartyJsLib.js 文件代码:
var Runoob;
(function(Runoob) {
var Calc = (function () {
function Calc() {
}
})
Calc.prototype.doSum = function (limit) {
var sum = 0;
for (var i = 0; i <= limit; i++) {
sum = sum + i;
}
return sum;
}
Runoob.Calc = Calc;
return Calc;
})(Runoob || (Runoob = {}));
var test = new Runoob.Calc();
如果我们想在 TypeScript 中引用上面的代码,则需要设置声明文件 Calc.d.ts,代码如下:
Calc.d.ts 文件代码:
declare module Runoob {
export class Calc {
doSum(limit:number) : number;
}
}
声明文件不包含实现,它只是类型声明,把声明文件加入到 TypeScript 中:
CalcTest.ts 文件代码:
///
var obj = new Runoob.Calc();
// obj.doSum("Hello"); // 编译错误
console.log(obj.doSum(10));
生成的 JavaScript 代码如下:
CalcTest.js 文件代码:
///
var obj = new Runoob.Calc();
//obj.doSum("Hello"); // 编译错误
console.log(obj.doSum(10));
最后我们编写一个 runoob.html 文件,引入 CalcTest.js 文件及第三方库 CalcThirdPartyJsLib.js:
浏览器打开该文件输出结果如下:
声明文件这一篇来自菜鸟教程