typescript中的数据类型
1.基础类型 string,number,null,undefined,boolean
undefined:一个没被分配值的变量
null:人为分配一个空值
strictNullChecks: tsconfig.json中配置项,值为on时,在使用可能是null的值前,需要显示的检查
2.数组类型Array<T>,T代表数组中的元素类型(本写法要求元素类型统一)
3.any,unknown,noImplictAny
unknown:是any的替代品,将unknown类型变量赋值给其它类型变量时,会报错。
noImplictAny:(implict:隐式 inplict:显式):项目中是否允许隐式any,tsconfig.json中配置项
类型标注 let len:Number = 11; //也可写成let len = 11,ts将进行类型推导 窄化??
函数:规定输入输出值类型
function greet(name:string):number{
console.log(name)
}
匿名函数 contexture typing:根据上下文猜测匿名函数参数的类型
const arr = ['frist','second','third'];
arr.forEach(function(s){ //匿名函数中当前项s,通过上下文推导出为string类型,所以支持toUpperCase方法
console.log(s.toUpperCase())
})
联合类型:ts会针对联合类型做排除法,联合类型只能使用两个类型公共的方法。
function greet(name:string | number):number{
// 只能使用传入类型公共的方法
if(typeof name === 'string'){ console.log(id); return;}
console.log(name.toUpperCase());
}
类型别名type:支持多种类型组合(&),可以简单理解为字符串的替换type id=number | string;
type Point = {x:name;y:number;}
type Bear = Point & {honey:boolean} //多种类型组合(&)
let uid :id = 100 //可以简单理解为字符串的替换, typeof uid 会返回number而不是id
type更多用法:
type StringOrNumber = string | number;
type Text = string | { text: string };
type NameLookup = Dictionary<string, Person>;
type Callback<T> = (data: T) => void; // 对T进行Callback操作
type Pair<T> = [T, T];
type Coordinates = Pair<number>;
type Tree<T> = T | { left: Tree<T>, right: Tree<T> };
接口interface:支持继承extends,接口被定义两次时定义的属性会合并(但相同属性不能更改类型,高阶能力)
interface Point{ x:name;y:number; }
interface Bear extends Point{ honey:boolean } //继承
类型别名和接口,都可以按以下方式调用:
const bear1:Bear = {
x:12,
y:'lele'
}
console.log(bear1)
更多知识见 《typescript中的interface和type到底有什么区别详解》https://www.jb51.net/article/163299.htm
类型断言assertion,只是提示作用
const x = 'hello' as number // 可使用 const x = ('hello' as unknown) as string 来欺骗ts
html元素对应的接口名称
字面类型:常量const的值为字面值,不能修改。相对于let 。
可以用字面类型约束一些特殊的函数:
interface options {
id:number
}
// 字面类型
function configure(x:options | 'auto'){
console.log(x)
}
configure('auto');
configure({id:1});
configure(''); //类型“""”的参数不能赋给类型“options | "auto"”的参数。
字面类型的坑点:只能处理当前一级传值
function handleReq(url:string,method:"GET"|"POST"){
console.log(url,method)
}
const req = { url:'http://abd.com',method:"GET" };
// handleReq(req.url, req.method); // 报错类型“string”的参数不能赋给类型“"GET" | "POST"”的参数。
正确写法0:
handleReq(req.url,"GET")
正确方法1:
handleReq(req.url,req.method as "GET")
正确方法2:
const req2 = { url:'http://abd.com',method:"GET" as "GET"};
handleReq(req.url,req.method)
正确方法3:
const req2 = { url:'http://abd.com',method:"GET"} as const;
handleReq(req2.url,req2.method)
js对象逃避类型检查方式
strictNullChecks:true时,需要显式的检查可能是null的值
function doSomeThing(x:string | null){
//报错: 对象可能为 "null"。
!.toUpperCase()); // 可以用!操作符,来断言某个值不是空值
}
枚举类型 enum:枚举类型默认会返回数字,支持反向取属性(也可以赋值为字符串,但是没必要,因为可以反向获取;也可以混合不同的数据类型,但是会增加复杂度)
enum Direction {
up=1,
down,
left,
right
}
console.log(Direction.down,Direction.left) //2 3 返回数字
console.log(Direction[Direction.left]) //left 反向操作提取Enumerate中的字符串(属性名称),也叫Reverse Mapping
function f(obj:{up:number}){
return obj.up
}
f(E)
f({up:1,down:2}) // Error 类型“{ up: number; down: number; }”的参数不能赋给类型“{ up: number; }”的参数。对象文字可以只指定已知属性,并且“down”不在类型“{ up: number; }”中。
泛型:一种抽象一类事物的共性特征的编程手段,它允许将类型作为其它类型的参数(表现形式),从而分离不同关注点的实现(作用)。泛型可以根据传入的函数的参数,自动推导出泛型类型和函数返回值类型。
关注点(interest Point)不同:程序里有三种表达:
接口interface:一种方面的描述,一个类型可以拥有多个方面的特性
继承inheritance:一种强表达。松树->extends->树
泛型Generics:是对共性的提取。e.g 在造床这件事务上,桃木,红木等等木头都有这个作用
e.g
// 以下方式,只能处理number类型
function identity1(arg:number):number{
return arg;
}
// 泛型函数
// 通过钻石操作符<>,传入泛型的参数,结合函数的参数(类型)和函数的返回值类型,帮我们支持所有类型
function identity2<Type>(arg:Type):Type{
return arg
}
let out2 = identity2<string>('MyString'); //等价于 let out = identity2('MyString'); 此时,可以通过值推导出传入值类型
// out2 = 100; //不能将类型“number”分配给类型“string”。
// 泛型类
class GenericNumber<T>{
private zeroValue:T;
constructor(v:T){
this.zeroValue = v;
}
public add(x:T,y:T){
return this.zeroValue + '' + x + y; //运算符“+”不能应用于类型“T”和“T”。=> 因为仅数字能进行运算
}
}
let gener = new GenericNumber<string>('hello,');
console.log(gener.add('Lucy','Lily')); //hello,LucyLily
// extends interface 给泛型添加约束
interface Lengthwise{
length:number
}
function loggingIdentity<Type extends Lengthwise>(arg: Type):Type{ // 让Type继承Lengthwise以支持.length
console.log(arg.length); // <Type extends Lengthwise>改成<Type>时,因不确定Type类型,所以找不到.length属性,导致报错
return arg
}
loggingIdentity<string>('test') // 也可简写为 loggingIdentity('test') ,通过传入值自动推导Type
// type keyof 给泛型添加约束
type Point = {x:number;y:number}
type P = keyof Point; //P = 'x' | 'y',即Point中所有的参数(属性)
function getProperty<Type,Key extends keyof Type>(obj:Type,key:Key){
console.log(obj[key])
return obj[key]
}
let x1 = {a:1,b:2,c:3,d:4}; //因为是静态的,所以可以用keyof操作符获取所有的key,若对象x1的类型为any,那么keyof就没有意义了
// x1.z= 10; // 类型“{ a: number; b: number; c: number; d: number; }”上不存在属性“z”。
getProperty(x1,"d")
// getProperty(x1,"m") //类型“"m"”的参数不能赋给类型“"d" | "a" | "b" | "c"”的参数。
实例化泛型类型,将类作为参数传入
function create<Type>(c:{new ():Type}):Type{ // 也可写为function create<Type>(c:new ()=>Type):Type{
return new c();
}
create(Foo) //返回Foo的实力
e.g 实战实例
class BeeKeeper {
hasMask : boolean = true;
}
class ZooKeeper {
nametags: string = 'Mikle';
}
class Animal {
numLegs : number = 4;
}
class Bee extends Animal{
keeper:BeeKeeper = new BeeKeeper();
}
class Lion extends Animal{
keeper:ZooKeeper = new ZooKeeper();
}
function createInstance<A extends Animal>(c: new()=>A):A{
return new c();
}
console.log( createInstance(Lion).keeper.nametags ); //返回一个
console.log( createInstance(Bee).keeper.hasMask );
窄化(Type Narrowing):本质是重新定义类型,解决联合类型校验的问题
if + typeof:
typeof返回值类型:string","number","bigint","boolean","symbol","undefined","object","function"。注意typeof null==='object'
真值窄化:帮我们更好的应对null,undefined,0等情况的判断
JavaScript真值表(if时会进else的):0,null,undefined,NaN," "(the empty string),0n(the bigint version of zero)
相等性窄化:隐式的窄化方法,相等性窄化。===,!==,== ,and != 都可以用来窄化类型。
interface Container{
value:number | null | undefined;
}
function mulValue(container:Container,factory:number){
if(container.value !=null){ //此处!=可以过滤掉null,undefined两种类型(!=两侧的 undefined和null都会转成false )。如果替换为!==,会报错 ,因为到时Container.value为 number或undefined
container.value *= factory;
return container.value
}
return container.value;
}
//number类型
let data = mulValue({value:10},2);
console.log(data)
//null或undefined类型
let data2 = mulValue({value:undefined},2);
console.log(data2)
in操作符窄化(type定义时)
type Fish = { swim:()=>void };
type Bird = { fly:()=>void };
function move(animal:Fish | Bird){
if('swim' in animal) return animal.swim();
else animal.fly()
}
class myFish{
public swim(){
return 'swim'
}
}
console.log(move(new myFish()));
// instanceof 做窄化,
// 此处Date必须是真实存在的Function类型,不能是type
function move1(time: Date | string){
if(time instanceof Date){
console.log('time',time)
return 'time'
}
}
move1(new Date());
组合类型推导
let x = Math.random() < 0.5 ? 10: "hello world!"; // 系统会自动推导出x为number或string类型
// xx = new Date() //Error 不能将类型“Date”分配给类型“string | number”。 因为ts推导出 只能赋值为number或string类型
控制流分析:
ts是如何做窄化的?在语法分析阶段,ts编译器识别出类型卫兵表达式(e.g if(typeof padding === 'number' ) 、instanceof).然后对返回值逻辑分别做窄化,窄化的本质是重新定义类型。
// 控制流窄化
function exp1(){
let x :string | number | boolean;
x = Math.random()>0.5; // boolean
if(Math.random()<0.5){
x='hello'; // string
console.log(2,typeof x);
}
else{
x = 100; // number
console.log(3,typeof x);
}
return x; 没有Boolean了,因为上面的if,else语句已经把Boolean的可能干掉了
}
exp1();
断言操作符:
Assertion操作符as:提示ts某种类型是什么,当用户比ts更了解类型时使用,as影响的是当前函数内部
predicate操作符is:用户自定义的类型守卫,用于帮助Typescript Narrowing ,is影响的是当前函数的调用逻辑,函数内无效
function isFish(pet:Fish | Bird) : pet is Fish {
// return pet.swim !== undefined // Error 类型“Bird”上不存在属性“swim”,因为pet is Fish
pet as Fish).swim !== undefined; //内部窄化逻辑
}
let pet = {
fly:()=>{}
}
if(isFish(pet)){
pet.swim() //上面有了 pet is Fish,才不报错 Bird无swim方法
}
else pet.fly();
判别的联合窄化(Discriminated unions)
interface Circle{ // 圆
kind:'circle';
radius:number;
}
interface Square{ //方形
kind:'square',
sideLength:number;
}
type Shape = Circle | Square;
// 计算面积
function getArea(shape : Shape){
switch(shape.kind){
case 'circle':
return Math.PI * shape.radius ** 2; // ** 平方
case 'square':
return shape.sideLength ** 2;
}
}
Nevel:不应该出现的类型
interface Circle{ // 圆
kind:'circle';
radius:number;
}
interface Square{ //方形
kind:'square',
sideLength:number;
}
interface Trips{
kind:'trips';
value:number
}
type Shape = Circle | Square | Trips;
// 计算面积
function getArea(shape : Shape){
switch(shape.kind){
case 'circle':
return Math.PI * shape.radius ** 2; // ** 平方
case 'square':
return shape.sideLength ** 2;
default:
Error 不能将类型“Trips”分配给类型“never”。
return _value;
}
}
inter 类型推导
// e.g 提取多层数组里的值
type Flatterned<T> = T extends Array<infer V>?Flatterned<V>:T;
function flattern<T extends Array<any>>(arr:T):Array<Flatterned<T>>{
return (new Array()).concat(...arr.map(x=>Array.isArray(x)?flattern(x):x))
}
let val = flattern([1,2,4,[5,[6,[7,[[[9]]]]]]]);
console.log(val) // [1, 2, 4, 5,6, 7, 9]