了解ts
是什么?
ts是js的超集,是静态类型语言
为什么要使用他?
- 他解决了js弱类型,没有命名空间导致难以模块化,不适合大型应用开发
- 静态类型可以在代码没有执行之前就可以检测出类型错误等问题,减少开发中变量名称打错,和变量上不存在的属性和方法的bug
- 智能提示,在变量后面输入一个点就会提示当前变量拥有的属性和方法
- 利用ts的关键词module 定义空间,export可以控制是否被外部访问
- 易上手,他没有抛弃js而是做成了js的超集完美兼容了js的语法使他的学习成本很低
- 可以定义一些声明声明已有的变量和类型,这样可以很方便的用强类型调用第三方库
- 新增了接口,类,枚举,泛型,方法重载等语法糖,用简洁等语法方便了js的使用
怎么使用
- 安装
npm install -g typescript - 使用方法,创建一个.ts文件写一些ts代码,使用下面的命令编译他,便可以直接通过script标签在html里引入编译后的文件直接使用
tsc [filename] - 创建一个练习ts的demo环境
- 创建一个目录 demo
- 在目录里创建一个package.js,在里面加入以下代码,index.ts 对应你创建的入口文件名称,编译后的文件会以当前文件名称命名修改它的后缀.ts 为.js
"scripts": {"build":"tsc ./index.ts && node ./index.js"}
- 写好代码后,想编译看结果输入
npm run build
ts语法
基本类型
- 字符串
let a:string = 'str' - 数值
let a:number = 123 - 布尔
let a:boolean = flase - 数组
let a:numner[] = [12,2,4] //前面的number定义数组里每个元素的类型 - 元组
let a:[string,numner] = ['str',12] //需要限定数组长度和每个元素的类型可以用元组 - 枚举
enum Color {red,black,yellow} ;let a = Color.red //为一组数值赋予友好的名字,并且可以给每一个元素赋默认值例如 red = 1 - any
在编程阶段不能确定类型的变量,可以把它赋值为any - void
函数没有返回值的时候我们可以把函数的返回类型设置为void - unll 和 undefined
let a:number = 12;a = null //默认情况null 和undefined是其他类型的子类,在--strictNullChecks 模式中 只能赋值给自身 - never
let a = ():never=>new Error('err') //nerver是所有类型的子类但没有类型是never的子类,在哪些总是抛出异常或是无法达到终点的函数返回值上设置never
变量声明
声明一个变量把值赋给它,值可以是基本类型或引用类型
- var
var a = 123 //var 声明的变量只存在全局作用域和函数作用域,他在预编译阶段会把变量提升到当前作用域到顶部 - let
let a = 123 //let 声明的变量存在块级作用域 - const
const A_A = 121 //const 声明的变量是常量存在块级作用域其引用值是不能被修改的
接口
对值拥有的结构进行类型检查
- 基本使用
interface Params{
name:string
age:number
}
let submit = (values:Params)=>{...} //传入的参数如果没有name和age便会报错,或传了不是name和age的参数也会报错
2.可选属性
interface Params{
name?:string //属性名称后面加上一个?设置这个属性是可传可不传的
age:number
}
3.只读属性
interface Params{
readonly age:number //设置当前属性是只能读不能修改的
}
let obj:Params = {age:1231}
obj.age = 123 //修改只读属性便会报错
4.额外的属性检查
interface Params{
name:string
age:number
[propsName:string]:any //propsName可以随便命名,添加了这个属性后就可以传除了name和age外的其他参数进去
}
let submit = (values:Params)=>{...}
5.函数类型
interface Params{
(name:string,age:number):void
}
let submit:Params = (values)=>{...}
6.可索引类型
interface Params{
[index:number]:string
}
let arr:Params = ['1','3']
7.类类型
类分静态部分的类型和实例部分的类型
//默认这样定义的接口只能检查实例的类型
interface Params {
name:string,
change:(event:any)=>void
}
class Objserver implements Params {
name : 'objserver'
change()=>{
console.log("change")
}
}
//想要静态部分和实例部分都检查需要写成以下形式
interface ParamsConstructor{
new (name:string,age:number):Params
}
interface Params {
change: ()=>void
}
class Objserver implements Params {
name: 'objserver'
constructor(name,age){}
change(){
console.log("change")
}
}
interface MachineProps{
(objserver:ParamsConstructor,name:string,age:number):Params
}
let machine:MachineProps=(objserver,name,age)=> {
return new Objserver(name,age)
}
let result = machine(Objserver, 'wag', 12)
console.log('result',result.change())
8.接口继承
interface parent{
color: string
}
interface child extends parent{
rap:string
}
let childInt:child = {rap:'adsa',color:'121'}
console.log('child',childInt)
9.混合类型
interface Concat {
(start: number): number
name: string
reload():void
}
function Machine():Concat {
let concat = <Concat>function(state) {
return state
}
concat.name = 'concat'
concat.reload = function () {
console.log('reload')
}
return concat
}
let result = Machine()
console.log('Machine',result)
console.log('name',result.name)
console.log('reload',result.reload())
10.接口继承类
class Parent{
private name:string
}
interface ChildProps extends Parent{
age:number
}
class Child extends Parent implements ChildProps{
age=123
}
console.log('Child',(new Child()).age)
类
1.类
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
return "Hello, " + this.greeting;
}
}
let greeter = new Greeter("world");
2.继承
class Parent {
constructor() { }
handle() {
console.log('parent')
}
}
class Child extends Parent {
constructor() { super() }
childHandle() {
console.log('childHandle')
}
}
let obj = new Child()
obj.handle()
obj.childHandle()
3.设置类,公共,私有,受保护,属性
公共 public //所有属性默认是公共的
私有 private //只能在基类里面访问
受保护 protected //只能在基类和派生类里面访问
只读 readonly //只读不能修改的
4.存取器
var InputValidate = /** @class */ (function () {
function InputValidate() {
}
Object.defineProperty(InputValidate.prototype, "value", {
get: function () {
return this._value;
},
set: function (val) {
if (typeof val === 'number') {
this._value = val;
}
else {
throw new Error('输入的不是number类型');
}
},
enumerable: false,
configurable: true
});
return InputValidate;
}());
var obj = new InputValidate();
console.log('get', obj.value);
5.抽象类
abstract class Parent {
name:string
constructor(naem: string) {
this.name = naem
}
handle() {
console.log('handle')
}
abstract childHandle(name:string):void
}
class Child extends Parent{
constructor(name: string) {
super('name')
}
childHandle() {
console.log('childHandle')
}
}
let obj = new Child('wang')
obj.handle()
obj.childHandle()
函数
1.基本使用
function test(name:string,age:number):void{...}
2.传递多个参数
let argFn = function (name:string,...arg:any[]) {
console.log('arg',arg)
}
argFn('wang',1,2,3,4,5)
3.this
let obj = {
name: 'jacky',
age: 22,
method: function () {
return () => console.log('name:',this.name)
}
}
obj.method()()
4.重载
当需要对传递不同类型的参数返回不同的数据类型,进行类型定义时用以下写法
function action(params: number): number[];
function action(params: string): string;
function action(params):any {
if (typeof params === 'string') {
return 'string'
}
if (typeof params === 'number') {
return [1,2,3]
}
}
泛型
泛型使得一个组件可以支持多种数据类型,用户可以使用自己的数据类型来使用组件 1.基本使用
function test<T>(value:T):T{return value}
2.泛型接口
interface TestProps {
<T>(value:T):T
}
let test: TestProps = function <T>(value: T): T{ return value };
console.log('test',test<string>('string'))
3.泛型类
class Parent<T> {
name:T;
setName = (val:T):T=>val
}
let obj = new Parent<string>()
console.log(obj.setName('jacky'))
4.泛型约束
当我们想操作某些值时并且我们知道它有,但编译器并不知道每种传进来的类型都有length属性,所有直接使用时编译器会报错 ,在下面的例子中通过了extends关键词 给当前泛型扩展了 TestProps接口使编译器可以确认他有length属性
interface TestProps{
length:number
}
function test<T extends TestProps>(params:T):T {
console.log(params.length)
return params
}
test({length:4,name:'jacky'})
5.泛型工厂类
function create<T>(c: {new(props): T; },props:{name:string}): T {
return new c(props);
}
class Parent {
name :string
constructor(props:any) {
this.name = props.name
}
}
let obj = create<Parent>(Parent, { name: 'jacky' })
console.log(obj)
枚举
当要定义一组相同类型的常量可以用枚举,例如我要定义一个枚举苹果,苹果有不同的颜色,那么就可以在这个枚举的里面定义不同颜色的苹果元素 1.基本枚举 string
enum Apple {
red = 'RED',
green ='GREEN'
}
console.log('apple',Object.keys(Apple)) //读取它的每个元素的key
//打印出来的值 ['red', 'green' ]
2.数值枚举
enum Apple {
red =0 , //不设置默认也会=0
green
}
console.log('apple',Object.keys(Apple)) //读取它的每个元素的key
//打印出来的值 [ '0', '1', 'red', 'green' ]
2.混合枚举
enum Apple {
red =0 , //不设置默认也会=0
green ,
name ='JACKY'
}
console.log('apple',Object.keys(Apple)) //读取它的每个元素的key
//打印出来的值 [ '0', '1', 'red', 'green', 'name' ]
类型推论
在没有指明的地方类型推论会给我们推论出当前类型 1.基础
let a = 12; //这里a的类型会被推断为 number
2.联合类型推论
let a = [1,2,'jacky'] //这里a会被推断为 (number|string)[]
3.上下文类型
function test(props){
console.log(props.name) //这里会报错,类型推论通过上下文归类找不到props的类型时就会报错,在不确定要传递什么类型时可以给他赋值一个any类型便不会报错
}
类型兼容性
1.变量兼容
要把一个类型赋值给另外一个类型,必须是这个类型包含另外一个类型
interface TestProps{
name:string
}
let x: TestProps
let y = { name: 'jacky', age: 123,child:121 }
x = y
//x.age = 12212 错误 -> 在这里除了接口定义的属性是可以修改的其他的都是不容许修改或添加的
console.log(x)
2.函数兼容
//1 传参,在一个函数和另外一个函数比较是在参数对应的位置有并且类型是一样的那么就可以把这个函数赋值给另外一个函数
let test1 = (name:string,age:number) => 0
let test2 = (name: string) => 0
test1 = test2
//2 返回值,这个函数发返回类型必须包含那个函数,才能把这个函数赋值给那个函数
let test3 = () => {name:'jakcy'}
let test4 = () =>( {name:'jakcy',age:1212})
test = test4
3.枚举兼容
enum Color{
Red,
Black
}
enum Status { Ready, End };
let a = Color.Red
a = Status.Ready //error 不同的枚举之间是不兼容的
4.类兼容
类是通过实例成员进行比较的,静态成员和构造函数不在比较之内,并且私有成员和受保护成员会影响兼容
class Parent {
name:string
constructor(parameters) {
}
}
class Child {
name:string
constructor(parameters) {
}
}
let a : Parent
let b: Child
b = a // ok
a = b // ok
5.泛型
1.在没有成员时泛型变量没有被使用,所以默认是的any类型 可以被互相赋值
interface TestProps<T>{
}
let a :TestProps<number>
let b: TestProps<string>
b = a //ok
2.给它设置了属性并且绑定泛型变量在使用时给泛型设置了不同的类型,这个时候类型固定,因为他们的类型不同所以他们不能互相赋值
interface TestProps<T>{
name:T
}
let a :TestProps<number>
let b: TestProps<string>
b = a //error
高级类型
1.交叉类型
function extend<T,U>(before:T,after:U) :T&U {
let result = <T & U>{}
if (before) {
for (let item in before) {
(<any>result)[item] = before[item]
}
}
if (after) {
for (let item in after) {
(<any>result)[item] = after[item]
}
}
return result
}
let result = extend({ name: 'jacky', age: 12, }, {age:1231231, child: 'children' })
console.log('result',result)
2.类型断言
interface Bird {
fly();
layEggs();
}
interface Fish {
swim();
layEggs();
}
function getSmallPet(val): Fish | Bird {
return val ?{
swim:()=>{},
layEggs:()=>{},
} : {
fly:()=>{},
layEggs:()=>{},
}
}
let getPet = getSmallPet(1);
(getPet as Fish ).swim()