Continue to create, accelerate growth! This is the second day of my participation in the "Nuggets Daily New Plan · June Update Challenge",Click to view event details
9. Type Protection
Automatically identify variable attributes and methods by identifying the executed code block by judgment
1. typeof
Type Protection
function double(val: number | string) {
if (typeof val === 'number') {
val
} else {
val
}
}
复制代码
2. instanceof
Type Protection
class Cat { }
class Dog { }
const getInstance = (clazz: { new(): Cat | Dog }) => {
return new clazz();
}
let r = getInstance(Cat);
if(r instanceof Cat){
r
}else{
r
}
复制代码
3. in
Type Protection
interface Fish {
swiming: string,
}
interface Bird {
fly: string,
leg: number
}
function getType(animal: Fish | Bird) {
if ('swiming' in animal) {
animal // Fish
} else {
animal // Bird
}
}
复制代码
4. Identifiable union types
interface WarningButton {
class: 'warning'
}
interface DangerButton {
class: 'danger'
}
function createButton(button: WarningButton | DangerButton) {
if (button.class == 'warning') {
button // WarningButton
} else {
button // DangerButton
}
}
复制代码
Five.null protection
const addPrefix = (num?: number) => {
num = num || 1.1;
function prefix(fix: string) {
return fix + num?.toFixed()
}
return prefix('zf');
}
console.log(addPrefix());
复制代码
It should be noted here that ts cannot detect inner function variable types
6. Custom Type Protection
interface Fish {
swiming: string,
}
interface Bird {
fly: string,
leg: number
}
function isBird(animal: Fish | Bird):animal is Bird {
return 'swiming' in animal
}
function getAniaml (animal:Fish | Bird){
if(isBird(animal)){
animal
}else{
animal
}
}
复制代码
7. Integrity Protection
interface ICircle {
kind: 'circle',
r: number
}
interface IRant {
kind: 'rant',
width: number,
height: number
}
interface ISquare {
kind: 'square',
width: number
}
type Area = ICircle | IRant | ISquare
const isAssertion = (obj: never) => { }
const getArea = (obj: Area) => {
switch (obj.kind) {
case 'circle':
return 3.14 * obj.r ** 2
default:
return isAssertion(obj); // 必须实现所有逻辑
}
}
复制代码
10. Type inference
1. Assignment inference
Inferred when assigning, the type flows from right to left, and the variable type will be inferred according to the assignment
let str = 'wj';
let age = 11;
let boolean = true;
复制代码
2. Return value inference
Automatically infer function return type
function sum(a: string, b: string) {
return a + b;
}
sum('a','b');
复制代码
3. Function inference
Functions are inferred from left to right
type Sum = (a: string, b: string) => string;
const sum: Sum = (a, b) => a + b;
复制代码
4. Attribute inference
The type of the attribute can be inferred from the attribute value
let person = {
name:'zf',
age:11
}
let {name,age} = person;
复制代码
5. Type inversion
Variable types can be inferred using typeof
keywords
let person = {
name:'zf',
age:11
}
type Person = typeof person
复制代码
6. Index access operator
interface IPerson {
name:string,
age:number,
job:{
address:string
}
}
type job = IPerson['job']
复制代码
7. Type mapping
interface IPerson {
name:string,
age:number
}
type MapPerson = {[key in keyof IPerson]:IPerson[key]}
复制代码
11. Cross Type
Intersection Types is a combination of multiple types into one type
interface Person1 {
handsome: string,
}
interface Person2 {
high: string,
}
type P1P2 = Person1 & Person2;
let p: P1P2 = { handsome: '帅', high: '高' }
复制代码
Example: We provide two groups of people, one group is handsome and the other group is tall. We want to find their intersection => tall and handsome guy
- cross type
function mixin<T, K>(a: T, b: K): T & K {
return { ...a, ...b }
}
const x = mixin({ name: 'zf' }, { age: 11 })
复制代码
interface IPerson1 {
name:string,
age:number
}
interface IPerson2 {
name:number
age:number
}
type person = IPerson1 & IPerson2
let name!:never
let person:person = {name,age:11}; // 两个属性之间 string & number的值为never
复制代码
12. Condition Types
1. Basic use of conditional types
You can use extends
keywords and ternary expressions to achieve conditional judgment
interface Fish {
name1: string
}
interface Water {
name2: string
}
interface Bird {
name3: string
}
interface Sky {
name4: string
}
type Condition<T> = T extends Fish ? Water : Sky;
let con1: Condition<Fish> = { name2: '水' }
复制代码
2. Condition type distribution
let con2: Condition<Fish|Bird> = { name2: '水' }
复制代码
Here, each item is distributed in turn, and finally the union type is used as the result, which is equivalent to:
type c1 = Condition<Fish>;
type c2 = Condition<Bird>;
type c = c1 | c2
复制代码
3. Built-in condition type
- 1.
Exclude
Types of Exclusions
type Exclude<T, U> = T extends U ? never : T;
type MyExclude = Exclude<'1' | '2' | '3', '1' | '2'>
复制代码
- 2.
Extract
Extraction type
type Extract<T, U> = T extends U ? T : never;
type MyExtract = Extract<'1' | '2' | '3', '1' | '2'>
复制代码
- 3.
NoNullable
Non-empty detection
type NonNullable<T> = T extends null | undefined ? never : T
type MyNone = NonNullable<'a' | null | undefined>
复制代码
Four. infer type inference
- 1.
ReturnType
Return value type
function getUser(a: number, b: number) {
return { name: 'zf', age: 10 }
}
type ReturnType<T> = T extends (...args: any) => infer R ? R : never
type MyReturn = ReturnType<typeof getUser>
复制代码
- 2.
Parameters
Parameter type
type Parameters<T> = T extends (...args: infer R) => any ? R : any;
type MyParams = Parameters<typeof getUser>;
复制代码
- 3.
ConstructorParameters
Constructor parameter types
class Person {
constructor(name: string, age: number) { }
}
type ConstructorParameters<T> = T extends { new(...args: infer R): any } ? R : never
type MyConstructor = ConstructorParameters<typeof Person>
复制代码
- 4.
InstanceType
Instance Type
type InstanceType<T> = T extends { new(...args: any): infer R } ? R : any
type MyInstance = InstanceType<typeof Person>
复制代码
Five.infer practice
Convert array type to union type
type ElementOf<T> = T extends Array<infer E> ? E : never;
type TupleToUnion = ElementOf<[string, number, boolean]>;
复制代码
Convert the arguments of two functions to cross types
type T1 = { name: string };
type T2 = { age: number };
type ToIntersection<T> = T extends ([(x: infer U) => any, (x: infer U) => any]) ? U : never;
type t3 = ToIntersection<[(x:T1)=>any,(x:T2)=>any]>
复制代码
表示要把
T1
、T2
赋予给x,那么x的值就是T1
、T2
的交集。(参数是逆变的可以传父类)
TS的类型:TS主要是为了代码的安全性来考虑。所以所有的兼容性问题都要从安全性来考虑!
13.内置类型
一.Partial转化可选属性
interface Company {
num: number
}
interface Person {
name: string,
age: string,
company: Company
}
// type Partial<T> = { [K in keyof T]?: T[K] }; 实现原理
type PartialPerson = Partial<Person>;
复制代码
遍历所有的属性将属性设置为可选属性,但是无法实现深度转化!
type DeepPartial<T> = {
[K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K]
}
type DeepPartialPerson = DeepPartial<Person>;
复制代码
我们可以实现深度转化,如果值是对象继续深度转化。
二.Required转化必填属性
interface Company {
num: number
}
interface Person {
name: string,
age: string,
company: Company
}
type PartialPerson = Partial<Person>;
type Required<T> = {[K in keyof T]-?:T[K]}
type RequiredPerson = Required<PartialPerson>
复制代码
将所有的属性转化成必填属性
三.Readonly转化仅读属性
type Readonly<T> = { readonly [K in keyof T]: T[K] }
type RequiredPerson = Readonly<Person>
复制代码
将所有属性变为仅读状态
四.Pick挑选所需的属性
type Pick<T, U extends keyof T> = { [P in U]: T[P] }
type PickPerson = Pick<Person, 'name' | 'age'>
复制代码
在已有类型中挑选所需属性
五.Record记录类型
type Record<K extends keyof any, T> = { [P in K] : T }
let person: Record<string, any> = { name: 'zf', age: 11 };
复制代码
实现map方法,我们经常用record类型表示映射类型
function map<T extends keyof any, K, U>(obj: Record<T, K>, callback: (item: K, key: T) => U) {
let result = {} as Record<T, U>
for (let key in obj) {
result[key] = callback(obj[key], key)
}
return result
}
const r = map({ name: 'zf', age: 11 }, (item, key) => {
return item
});
复制代码
六.Omit忽略属性
let person = {
name: 'wj',
age: 11,
address: '回龙观'
}
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>
type OmitAddress = Omit<typeof person, 'address'>
复制代码
忽略person中的address属性 (先排除掉不需要的key,在通过key选出需要的属性)
14.装包和拆包
一.装包
type Proxy<T> = {
get():T,
set(value:T):void
}
type Proxify<T> = {
[P in keyof T]: Proxy<T[P]>
}
let props = {
name: 'wj',
age: 11
}
function proxify<T>(obj:T):Proxify<T>{
let result = {} as Proxify<T>;
for(let key in obj){
let value = obj[key];
result[key] = {
get(){
return value
},
set:(newValue)=>value = newValue
}
}
return result
}
let proxpProps = proxify(props);
复制代码
二.拆包
function unProxify<T>(proxpProps:Proxify<T>):T{
let result = {} as T;
for(let key in proxpProps){
let value = proxpProps[key];
result[key] = value.get()
}
return result
}
let proxy = unProxify(proxpProps)
复制代码
15.自定义类型
一.Diff实现
求两个对象不同的部分
let person1 = {
name: 'wj',
age: 11,
address: '回龙观'
}
let person2 = {
address: '回龙观',
}
type Diff<T extends object,K extends Object> = Omit<T,keyof K>
type DiffPerson = Diff<typeof person1,typeof person2>
复制代码
二.InterSection交集
let person1 = {
name: 'wj',
age: 11,
address: '回龙观'
}
let person2 = {
address: '回龙观',
}
type InterSection<T extends object, K extends object> = Pick<T, Extract<keyof T, keyof K>>
type InterSectionPerson = InterSection<typeof person1, typeof person2>
复制代码
三.Overwrite属性覆盖
type OldProps = { name: string, age: number, visible: boolean };
type NewProps = { age: string, other: string };
type Diff<T extends object,K extends Object> = Omit<T,keyof K>
type InterSection<T extends object, K extends object> = Pick<T, Extract<keyof T, keyof K>>
type Overwrite<T extends object, K extends object, I = Diff<T,K> & InterSection<K,T>> = Pick<I,keyof I>
type ReplaceProps = Overwrite<OldProps, NewProps>
复制代码
如果存在已有属性则使用新属性类型进行覆盖操作
四.Merge对象合并
type Compute<A extends any> = { [K in keyof A]: A[K] };
type Merge<T, K> = Compute<Omit<T, keyof K> & K>;
type MergeObj = Merge<OldProps,NewProps>
复制代码
将两个对象类型进行合并操作
16.unknown
一.unknown类型
unknown
类型,任何类型都可以赋值为unknown
类型。 它是 any 类型对应的安全类型
let unknown:unknown;
unknown = 'zf';
unknown = 11;
复制代码
不能访问unknown类型上的属性,不能作为函数、类来使用
-
联合类型中的
unknown
type UnionUnknown = unknown | null | string | number 复制代码
联合类型与
unknown
都是unknown
类型 -
交叉类型中的
unknown
type inter = unknown & null 复制代码
交叉类型与
unknown
都是其他类型
二.unknown特性
-
never是unknown的子类型
type isNever = never extends unknown ? true : false;= 复制代码
-
keyof unknown 是never
type key = keyof unknown; 复制代码
-
unknown类型不能被遍历
type IMap<T> = { [P in keyof T]:number } type t = IMap<unknown>; 复制代码
unknown类型不能和number类型进行
+
运算,可以用于等或不等操作
17.模块和命名空间
默认情况下 ,我们编写的代码处于全局命名空间中
一.模块
文件模块: 如果在你的 TypeScript 文件的根级别位置含有 import 或者 export,那么它会在这个文件中创建一个本地的作用域 。
// a.ts导出
export default 'zf'
// index.ts导入
import name from './a'
复制代码
二.命名空间
命名空间可以用于组织代码,避免文件内命名冲突
-
命名空间的使用
export namespace zoo { export class Dog { eat() { console.log('zoo dog'); } } } export namespace home { export class Dog { eat() { console.log('home dog'); } } } let dog_of_zoo = new zoo.Dog(); dog_of_zoo.eat(); let dog_of_home = new home.Dog(); dog_of_home.eat(); 复制代码
-
命名空间嵌套使用
export namespace zoo { export class Dog { eat() { console.log('zoo dog'); } } export namespace bear{ export const name = '熊' } } console.log(zoo.bear.name); 复制代码
命名空间中导出的变量可以通过命名空间使用。
18.类型声明
一.声明全局变量
- 普通类型声明
declare let age: number;
declare function sum(a: string, b: string): void;
declare class Animal { };
declare const enum Seaons{
Spring,
Summer,
Autumn,
Winter
}
declare interface Person {
name:string,
age:number
}
复制代码
类型声明在编译的时候都会被删除,不会影响真正的代码。目的是不重构原有的js代码,而且可以得到很好的TS支持
练习: 声明jQuery类型
jquery通过外部CDN方式引入,想在代码中直接使用
declare const $:(selector:string)=>{
height(num?:number):void
width(num?:number):void
};
$('').height();
复制代码
- 命名空间声明
declare namespace jQuery {
function ajax(url:string,otpions:object):void;
namespace fn {
function extend(obj:object):void
}
}
jQuery.ajax('/',{});
jQuery.fn.extend({});
复制代码
namespace
表示一个全局变量包含很多子属性 , 命名空间内部不需要使用 declare 声明属性或方法
二. 类型声明文件
类型声明文件以
.d.ts
结尾。默认在项目编译时会查找所有以.d.ts
结尾的文件
// jquery.d.ts
declare const $:(selector:string)=>{
height(num?:number):void
width(num?:number):void
};
declare namespace jQuery {
function ajax(url:string,otpions:object):void;
namespace fn {
function extend(obj:object):void
}
}
复制代码
三.编写第三方声明文件
配置tsconfig.json
- jquery声明文件
"moduleResolution": "node",
"baseUrl": "./",
"paths": {
"*": ["types/*"]
},
复制代码
// types/jquery/index.d.ts
declare function jQuery(selector: string): HTMLElement;
declare namespace jQuery {
function ajax(url: string): void
}
export = jQuery;
复制代码
- events模块声明文件
import { EventEmitter } from "zf-events";
var e = new EventEmitter();
e.on('message', function (text) {
console.log(text)
})
e.emit('message', 'hello');
复制代码
export type Listener = (...args: any[]) => void;
export type Type = string | symbol
export class EventEmitter {
static defaultMaxListeners: number;
emit(type: Type, ...args: any[]): boolean;
addListener(type: Type, listener: Listener): this;
on(type: Type, listener: Listener): this;
once(type: Type, listener: Listener): this;
}
复制代码
四.模块导入导出
import $ from 'jquery' // 只适用于 export default $
const $ = require('jquery'); // 没有声明文件可以直接使用 require语法
import * as $ from 'jquery' // 为了支持 Commonjs规范 和 AMD规范 导出时采用export = jquery
import $ = require('jquery') // export = jquery 在commonjs规范中使用
复制代码
五.第三方声明文件
@types是一个约定的前缀,所有的第三方声明的类型库都会带有这样的前缀
npm install @types/jquery -S
复制代码
当使用jquery时默认会查找
node_modules/@types/jquery/index.d.ts
文件
查找规范
- node_modules/jquery/package.json 中的types字段
- node_modules/jquery/index.d.ts
- node_modules/@types/jquery/index.d.ts
19.扩展全局变量类型
一.扩展局部变量
可以直接使用接口对已有类型进行扩展
interface String {
double():string
}
String.prototype.double = function () {
return this as string + this;
}
let str = 'wj';
复制代码
interface Window {
mynane:string
}
console.log(window.mynane)
复制代码
二.模块内全局扩展
declare global{
interface String {
double():string;
}
interface Window{
myname:string
}
}
复制代码
声明全局表示对全局进行扩展
三.声明合并
同一名称的两个独立声明会被合并成一个单一声明,合并后的声明拥有原先两个声明的特性。
1.同名接口合并
interface Animal {
name:string
}
interface Animal {
age:number
}
let a:Animal = {name:'zf',age:10};
复制代码
2.命名空间的合并
-
扩展类
class Form {} namespace Form { export const type = 'form' } 复制代码
-
扩展方法
function getName(){} namespace getName { export const type = 'form' } 复制代码
-
扩展枚举类型
enum Seasons { Spring = 'Spring', Summer = 'Summer' } namespace Seasons{ export let Autum = 'Autum'; export let Winter = 'Winter' } 复制代码
3.交叉类型合并
import { createStore, Store } from 'redux';
type StoreWithExt = Store & {
ext:string
}
let store:StoreWithExt
复制代码
四.生成声明文件
配置
tsconfig.json
为true 生成声明文件
"declaration": true
复制代码
完结撒花
The above is all the TypeScript
basics . Mastering these is more than enough for the interview, but it still needs more practice for the language. Welcome to like and favorite!