认识泛型
//在定义这个函数时,我不决定这些函数的类型
//而是让调用者 以参数的形式告知 这里的函数参数应该是什么类型
function sum<Type>(num1:Type):Type{
return num1
}
//告诉函数,我要传以number类型的数据作为形参
sum<number>(20)
//告诉函数,我要传一个对象类型的参数,作为形参
sum<{name:string}>({name:"why"})
//数组类型,并且数组中数据是任意类型
sum<any[]>(["abc"])
//不写具体类型,直接传参,TS会自动推导,默认推导出是字面量类型
sum(50)
接收多个类型参数
//T是Type的缩写
function foo<T,E>(arg1: T, arg2: E){
}
foo<number,string>(10,"abc")
接收剩余参数
function foo<T,E>(arg1: T, arg2: E,...args:T[]){
}
//因为剩余参数用的是T,所以这里必须保持一致用number类型
foo<number,string>(10,"abc",1,2,3,4,5,6)
泛型就是让 使用者自己把控数据类型 ,如果已经确定数据类型了,其实不需要用泛型
泛型接口
//接口里面用到的类型是让用户自己决定的
//可以自己设置默认类型,后续使用如果不指定类型,就用默认类型
interface IPerson<T1=string,T2=number>{
name: T1
age: T2
}
//接口这里 没有类型推导,必须要自己指定
const p:IPerson<string,number> = {
name:"why",
age:18
}
泛型类的使用
class Point{
//x:number
//y:number
//z:number
x:T
y:T
z:T
constructor(x:T,y:T,z:T){
this.x = x
this.y = y
this.z = z
}
}
//这里的T 泛型为 number(类型推导)
const p = new Point(1,2,3)
//也可以自己指定
const p = new Point<number>(1,2,3)
//指定对象 是一个类的类型并且类的类型为泛型 类型
const p:Point<number> = new Point(1,2,3)
//数组类型
const name: string[] = ["abc","cba","nba"]
//这两种写法是等价的,但是这种写法更像泛型(不推荐)
const name: Array<string> = ["abc","cba"]
泛型的类型约束
function getLength<T>(arg: T){
//其实这样写是会有问题的,万一我传来的是number类型,他是没有length属性的
return arg.length
}
需要对类型进行限制
interface ILength{
//接口里面存储的是对象类型,要求有一个这样的对象属性才可以。
length:number
}
function getLength<T extends ILength>(arg: T){
return arg.length
}
getLength(123)
//不给你传,因为ILength接口中定义了length属性
//但是number类型中,不存在length属性,更别说是不是number类型了
getLength("abc")//可以传,因为他有length属性,并且length属性是number类型
getLength(["abc","cba"])//数组里也是有length属性的
getLength({length:100})//对象里书写了length属性
TypeScript其他内容补充
模块化开发
模块 math.ts
封装了一个独立的模块,将两个函数导出
export function add(num1:number1,num2:number){
return num1 + num2
}
export function sub(num1:number1,num2:number){
return num1 - num2
}
main.ts
导入模块
import {add,sub} from './math'
console.log(add(20,10))
console.log(sub(20,10))
命名空间
format.ts
一个文件里有不同功能,但是相同命名的方法
除了避免命名,也可以命名空间
export function format(time: string){
return "2222-02-22"
}
export function format(price: number){
return "99.99"
}
------------------------------------------
namespace time{
export function format(time: string){
return "2222-02-22"
}
//命名空间里可以定义很多东西,默认属于内部,外部想要拿到,必须要export出去
export function foo(){
}
export let name: string = "abc"
}
//在对命名空间进行export,就可以在其他文件中import了
export namespace price{
export function format(price: number){
return "99.99"
}
}
time.format
time.foo
time.name
price.format
大家能够避免同名,就避免,最好不要出现相同命名的情况。
类型查找
内置类型声明
当我们在vscode中安装TypeScript环境时,就会创建。
外部定义类型声明
同样都是引用,为什么lodash会报错?
import axios from 'axios'//不会报错
import lodash from 'lodash'//报错了
因为TS中并没有在d.ts中 声明lodash
自定义声明
那我想用lodash,就需要自己自定义类型声明
coderwhy.d.ts
declare module 'lodash'{
export function join(arr:any[]):void
}
main.ts
import lodash from 'lodash'//不会报错了
//可以使用自定义声明后的join方法
console.log(lodash.join(["abc","cba"]))
案例
自定义的.d.ts文件会转换为js文件,然后引用映射到.html文件中,最终呈现在页面上
index.html
<head></head>
<body>
<script>
//在html中定义的变量,本质是不可以在main.ts中使用的
//因为TS不知道你有name、age、height,你没有声明
let name = "why"
let age = 18
let height = 1.88
function whyFoo(){
console.log("whyFoo")
}
function Person(name,age){
this.name = name;
this.age = age;
}
</script>
</body>
why.d.ts(自定义声明)
//声明模块
declare let name: string
declare let age: number
declare let height: number
declare function whyFoo():void
declare class Person{
name:string
age:number
constructor(name:string,age:number)
}
main.ts
console.log(name)
console.log(age)
console.log(height)
whyFoo()
const p = new Person("why",18)
console.log(p)
在index.html中书写代码,在main.ts中能够使用
声明文件
main.ts
从外部引入一张图片
import Image from '...'
本质是会报错的,因为TS不认识你这张图片
.d.ts
//将以.jpg结尾的文件,全部当成模块来使用
declare module '.*jpg'
后续再在main.ts中引入就可以了
import Image from '.jpg'
声明命名空间
在我们的项目中引入JQuery,就需要用到命名空间
在.d.ts中
//因为我本身也有$,所以要用命名空间 解决重复命名
declare namespace ${
export function ajax(settings:any):any
}
main.ts
$.ajax({
})
总结
不管在TS中使用什么,前提:必须要被TS声明。可以来自内置类型声明、外部类型声明、自定义类型声明。