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元素對應的接口名稱 https://www.cnblogs.com/liuxu-xrl/p/15401274.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:
正確方法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){
console.log("hello,"+x.toUpperCase());
//報錯: 對象可能為 "null"。
console.log("hello,"+x
!.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;
//string | number,沒有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
return (
pet as Fish).swim !== undefined; //內部窄化邏輯
}
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:
const _value:never = shape; // 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]