安装TypeScipt编译器
npm i typescript -g
编译指令
tsc 1.ts --outDir ./dist
如果不加--outDir则默认会编译到ts同一目录
ts-node
tsconfig:
"./src":只编译src目录下文件,内部子文件夹不编译
"./src//":代表递归编译文件夹内部所有子文件夹 后面代表所有文件
ts-node:其他类型编译器 ts-node直接编译
ts数据类型
数字,字符串,布尔值
null underfined
数组 元组 枚举
void any Never
类型系统
string number boolean 基本类型
String Number Boolean 对象类型
基本类型可以赋值给包装类型,但是反之不可
数组(必须存储同一类型)
//基本语法
//数组的声明,此时push是没用的,因为未定义
// let arr:number[];
//数组的定义
let arr:number[]=[];
//泛型方式
// let arr1:Array<number>;
arr.push(...[1,2,4])
console.log(arr)
元组(类型不必相同)
let data:[number,string,boolean];
data=[1,"a",true];
说明:元组再3.1之后,不能越界使用联合类型了,而且赋值和定义的类型要一一对应
联合类型
//多个类型中的一个 或的关系
let a:string|number|boolean=20;
a="a";
console.log(a)
交叉类型
//多个类型的叠加,并且的关系
let b:string&number=
枚举
enum Color{
RED,
YELLOW
}
console.log(Color.RED);//0
console.log(Color.YELLOW);//1
enum Color{
RED=1,
YELLOW
}
console.log(Color.RED);//1
console.log(Color.YELLOW);//2
Never
说明:代表那些永远不存在的值的类型,ts也可以自动推断,never此时也可以省略
function err():never{
throw new Error("error")
}
Any
说明:任意类型,在不确定数据类型的情况下使用
let a:any="a";
a=10;
console.log(a)
函数
//函数表达式
let f:()=>string=function():string{
return "a"
}
let f:()=>void=function(){
}
//函数声明
function fn(x:number,y:number):number{
return x+y
}
可选参数和参数默认值
说明:通过?来定义可选参数
function fn(x:Type,y?:Type):Type
可选参数默认为undefined
可选参数必须在必传参数之后
//可选参数
function fn(x:number,y?:number):number{
return x+y
}
//参数默认值,其实因为类型推导,可直接写成y=1
function fn1(x:number,y:number=1):number{
return x+y
}
console.log(fn(1));//NaN
console.log(fn1(1));//2
补充:可选参数和默认值不要用在一个参数上
剩余参数
//剩余参数
function fn2(...arg:any[]){
console.log(arg) //[ 1, 2, 3 ]
}
fn2(1,2,3)
函数重载
//定义函数的重载格式
function fn(x:number,y:string);
function fn(x:number,y:number);
//定义函数具体的实现
function fn(x:any,y:any){
console.log(x+y)
}
fn(1,2) //3
fn(1,"2")//12
ts中的this
案例一:
let obj={
a:10,
fn(){
//函数中默认this指向是any,通过下面再配置文件中解决,而且如果是类似于
//document中事件的this,ts会自动推导出类型,this指向不需要下面配置也是事件对象
// "noImplicitThis": true
console.log(this.a)
//注意:此时this的指向只是是否有提示的问题,真的执行代码配置文件设置不设置值都是10
}
}
案例二:
let obj={
a:20,
fn(this:Document){
console.log(this.querySelector)
}
}
document.onclick=obj.fn
说明:如果配置配置了this指向"noImplicitThis": true,
则fn中的this指向就是obj对象,但是此时obj.fn指向了
点击事件,为了有提示信息,需要手动指定this指向this:Document
这个this参数其实是一个假参数,ts编译时候会被去掉,纯粹是
为了代码提示而存在,this指向配置不配置,修改不修改都不影响最后的结果
修饰符
public protected(该类和子类能访问) private(类,对象内部) readonly(类,对象内部可用,其他只读)
案例一:
class Person{
readonly n:number;
constructor(num:number){
this.n=num;
}
}
let p=new Person(20)
案例二:简写方式
class Person{
constructor(public num:number){
this.n=num;
}
}
let p=new Person(20)
说明:因为ts不同于js,构造函数中属性需要先声明才能
使用,此时public num:number此种方式就是相当于提前再
class中先声明了一份
存取器
class Person{
//私有属性,大家默认的规则是下划线
private _num:number;
//存取器
//存取器再ts中不是当做方法使用的,而是被当做属性
get num():number{
return this._num
}
set num(num:number){
if(num>0){
this._num=num;
}
}
}
let p=new Person()
p.num=-10
console.log(p.num);//undefined
静态
class Person {
private static instance;
private constructor() { }
public static getInstance() {
if (!Person.instance) {
Person.instance = new Person();
}
return Person.instance;
}
}
let p=Person.getInstance();//相等
let p1=Person.getInstance();
抽象类
abstract class Person {
constructor() { }
abstract study():void;
}
class Student extends Person{
study(): void {
console.log("学习");
}
}
let s=new Student();
s.study()
接口
//接口不能有任何属性和方法实现,只能有抽象描述
interface Options{
num:number;
//可选的
name?:string;
say();
}
class optImpl implements Options{
num: number;
constructor(num:number){
this.num=num;
}
say() {
console.log(this.num+"说话");
}
}
function fn(opts:Options) {
opts.say()
}
fn(new optImpl(20))
断言
interface Options{
num:number;
name:string;
}
function fn(opts:Options) {
}
//断言
//按理说必须传入{num:20,name:"呵呵"}类似的才能通过
//但是通过断言可强制判定传入参数是什么类型
fn({} as Options)
补充:
let obj={
num:10,
name:"saa",
a:1
}
fn(obj)
说明:如果把传入的参数先赋值好在传入,可以避免规则检测
不会报错,但是此种情况只能在传入的obj覆盖全部所需参数
情况下,也就是说只能多不能少
索引签名
/**
* 索引签名:
* 希望规则是:一组由数字进行key命名的对象
* 补充:索引签名的key类型只能是string或者number
* 索引签名在下面传参时候,是可有可没有的,而且不限制个数
*/
interface Options{
//key是number,value是any类型的数据
[atrr:number]:any;
}
function fn(opts:Options) {
}
fn({
0:1,
2:20
})
函数类型接口
/**
* 函数类型接口
* 是一个包含由fn并且值的类型为函数的结构体
* 并不是描述函数结构而是一个包含函数的对象结构
*/
interface Options{
fn:Function
}
let o:Options={
fn:function(){
}
}
/**
* 下面约定就是函数结构,而不是包含有函数的对象结构了
*/
interface IFn{
(x:number):number
}
let fn:IFn=function(x:number){return x}
/**
* 下面是函数结构的实践
* 因为interface的约定,保证了传参的正确性
* 在编译阶段避免了出错
*/
interface MouseEventCallBack{
(e:MouseEvent):any;
}
let fn1:MouseEventCallBack=function(e:MouseEvent){}
document.onclick=fn1;
补充案例
interface AjaxData{
code:number;
data:any;
}
interface AjaxCallBack{
(rs:AjaxData):any
}
function ajax(callback:AjaxCallBack){
callback({
code:200,
data:{}
})
}
泛型
/**
* 泛型:
* 很多时候,类型写死,不利于复用
*/
//泛型变量
function fn<T>(args:T):T{
return args;
}
function fn1<T,S>(args:T,args1:S):[T,S]{
return [args,args1];
}
//数组形式
function fn2<T>(args:T[]):T[]{
return args;
}
function fn3<T>(args:Array<T>){}
泛型类
class MyArray<T>{
private _data:T[]=[];
public push(v:T):number{
return this._data.length;
}
}
let a=new MyArray<string>();
a.push("a")
let b=new MyArray<number>();
b.push(1)
泛型类型
//泛型类型
let fn:<T>(x:T,y:T)=>number=function(x,y){
return Number(x)+Number(y)
}
let fn1=function<T,S>(x:T,y:S):number{
return Number(x)+Number(y)
}
console.log(fn(1,2));//3
console.log(fn1<number,string>(1,"2"));//3
泛型接口
interface IFN<T,S>{
(x:T,y:S):S
}
let fn2:IFN<string,number>=function(x,y){
return Number(x)+Number(y);
}
类类型
/**
* 类类型:
* 表示这个类型对应的对象
*/
//错误实例:此时Array代表就是类类型,但是需要的参数是该类
//对应的构造函数
function getArray1(constructor:Array<number>){
return new constructor();
}
getArray2(Array)
//补充:p后面的Person就是类类型
let p:Person=new Person();
//下面是构造函数
let fn1:{new ():Person}
//正确写法
function getArray2(constructor:{new():Array<string>}){
return new constructor();
}
getArray2(Array)
泛型约束
形式一:
function fn<T extends number>(a:T){
console.log(a);
}
形式二:
interface Len{
length:number
}
function fn1<T extends Len>(a:T){
console.log(a.length);
}
fn1("a")//此时在fn1(1)则会报错,因为数字类型没有length属性
装饰器
基本案例:
//配置文件(开启装饰器):"experimentalDecorators": true
/**
* 类装饰器:
* 类装饰器用于构造函数,可以用来监视,修改或者替换类定义
* 类的构造函数会作为类装饰器函数的唯一参数
* function f(constructor:Function){}
*/
function f<T extends {new (...args:any[]):{}}>(constructor:T):T{
class P extends constructor{
age=20
}
return P;
}
//f是装饰器函数,属于类装饰器,会自动调用,并且传入对应类
//的构造函数
@f
class Person{
name="qiang"
}
let p=new Person();
//从打印结果可知,其实p已经是P而不是Person了
console.log(p);//P { name: 'qiang', age: 20 }
类装饰器参数
function f(v:number){
return function f1<T extends {new (...args:any[]):{}}>(constructor:T):T{
class P extends constructor{
age=v;
}
return P;
}
}
@f(10)
class Person{}
let p=new Person()
console.log(p);//P { age: 10 }
说明:类装饰器修饰的类其实都是新类,例如此时是P而不是Person
因为类装饰器是js自动调用的,顾不可以加(),但是为了参数
可以利用闭包,返回真实类装饰器,通过间接方式传参
方法装饰器
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
@enumerable(false)
greet() {
return this.greeting;
}
}
function enumerable(value: boolean) {
/**
* target:对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
propertyKey:成员的名字(说白了,方法名)
descriptor: 成员的属性描述符,例如:是否可编辑,可修改,可读可写等
descriptor.value:就是贝装饰器修饰的方法==target.propertyKey
* */
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
let fn=descriptor.value;
descriptor.value=function(){
console.log("1");
//因为this绑定的原因,如果此处不给fn绑定this,则返回指是underfined
//而且当前this就是对应的对象或者类(静态成员的话)
let a=fn.apply(this);
console.log(a);
//不能通过下面方法调用,,否则会形成无止境的递归
// descriptor.value.apply(this)
console.log("2");
}
};
}
let g=new Greeter("abbb");
g.greet()
网友评论