TS Mount Everest 002 [Study Notes]

Generics /* * @Author: error: error: git config user.name & please set dead value or install git && error: git config user.email & please set dead value or install git &a…

Generics

/* * @Author: error: error: git config user.name & please set dead value or install git && error: git config user.email & please set dead value or install git & please set dead value or install git * @Date: 2025-03-19 10:42:31 * @LastEditors: error: error: git config user.name & please set dead value or install git && error: git config user.email & please set dead value or install git & please set dead value or install git * @LastEditTime: 2025-03-19 15:11:32 * @FilePath: /ts-classes/src/index.ts * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE */// Genericsclass Animal {  constructor(public name: string, public age: number) {}}class Person {  constructor(public name: string, public age1: number) {}}// In TypeScript, when using it, you can usefunction createInstance<T>(c: { new (...args: any[]): T }, ...args: any[]): T {  return new c(...args);}// const animal = new Animal('dog', 12);// console.log(animal.name);// console.log(animal.age);const animal = createInstance<Animal>(Animal, 'dog', 12);const person = createInstance(Person, 'Tom', 12);// If the type is uncertain, we can determine it based on generics.// It's important to note that TypeScript does not execute; it only performs type checking during compilation.// Generate an array of corresponding length based on the provided data.function createArray<T>(length: number, value: T): T[] {  return Array.from({ length }, () => value);}// Swap the values of two variables.type ISwap = <T, U>(tuple: [T, U]) => [U, T];interface ISwap2 {  <T, U>(tuple: [T, U]): [U, T]; // Why are <T, U> written here instead of after the function? When generics are used, types can be passed and inferred, but the type is not determined during internal calls.}// Writing it at the beginning of the definition means passing parameters when using the type; writing it at the beginning of the function means passing parameters when calling the function.// Is the generic passed when using the type, or when calling the function?const swap: ISwap = (tuple) => {  return [tuple[1], tuple[0]];};const swapArray = swap<number, string>([1, '2']);// Generics have default values and are used with some union types.type UnionType<T = boolean> = T | number | string;let union: UnionType = '123';// Generic constraints require that the passed parameters meet the requirements. A extends B means A is a subtype (or the same type) of B.interface ILength {  length: number;}// What is a child? What is a parent?function getLength<T extends ILength>(value: T) {  // As long as my object has a length property, it's fine.  return value.length;}getLength('123');getLength({ length: 123 });function getValue<T, K extends keyof T>(obj: T, key: K) {  return obj[key];}getValue({ name: '张三', age: 12 }, 'age');interface ILoginResponse<T> {  code: number;  message?: string;  data: T;}interface ILoginData {  token: string;  userInfo: {    name: string;    age: number;  };  roles: string[];}function toLogin<T>(response: ILoginResponse<T>) {  return response.data;}const loginResponse = toLogin<ILoginData>({  code: 200,  data: {    token: '123',    userInfo: {      name: '张三',      age: 12,    },    roles: ['admin', 'user'],  },});// Using generics in classesclass Cache<T> {  private data: T[] = [];  add(item: T) {    this.data.push(item);  }}const cache = new Cache<string>();cache.add('123');cache.add('456');export {};

Intersection Types

// & Intersection Type | Union Type// Combines multiple types into one type.interface Person1 {  handsome: string;}interface Person2 {  high: string;}// A tall and handsome person (intersection type)type Person = Person1 & Person2;const person: Person = {  // handsome: '帅',  high: '高',};// An intersection type can be assigned to any supertype.export {};

unkown

// unknown type// unknown is the safe type of any; when a generic type is not specified, it defaults to unknown.let a: unknown;// By default, unknown must be type-checked before it can be used (type checking or assertion).// Type assertionlet b = a as string;// unknown cannot directly call methods; assertion is required.function processInput(val: unknown) {  if (typeof val === 'string') {    console.log(val.toUpperCase());  } else if (typeof val === 'number') {    console.log(val.toFixed(2));  } else {    console.log('unknown');  }}let name: unknown = 'String字符串';// name is of unknown type, cannot directly call methods.(name as string).toUpperCase();// Characteristics of unknown in union or intersection typestype UnionType = unknown | string; // unknown typetype IntersectionType = unknown & string; // string typelet union: UnionType = 'String字符串';let intersection: IntersectionType = 'String字符串';export {};

Conditional Types

// Conditional Types// Conditional types are a type operator in TypeScript that selects different types based on the result of a conditional expression.type ResStatusMessage<T extends number> = T extends 200 | 204 | 206  ? 'success'  : 'error';// type R1 = ResStatusMessage<'abc'>; // The status code must be a number; passing it this way will not cause an error.type R2 = ResStatusMessage<200>; // The status code must be a number; passing it this way will not cause an error.type Condition<T, U> = T extends U ? 'success' : 'fail';type R3 = Condition<'abc', string>; // Type inferred as 'success'type R4 = Condition<'abc', number>; // Type inferred as 'fail'// Application of conditional types in functionsinterface Bird {  fly: () => void;}interface Sky {  name: '天空';}interface Fish {  swim: () => void;}interface Water {  name: '水';}type ConditionType<T> = T extends Bird ? Sky : Water;type R5 = ConditionType<Bird>; // Type inferred as Skytype R6 = ConditionType<Fish>; // Type inferred as Watertype FromatReturnType<T extends string | number> = T extends number  ? number  : T extends string  ? string  : never;// Generics generally represent whether the input is a definite (infinite) constraint, function overloading (finite).function sum<T extends number | string>(a: T, b: T): FromatReturnType<T> {  // function sum<T extends number | string>(a: T, b: T):T {  // If the return value is a number type, return a number type; if it's a string type, return a string type. But T cannot be written here as the return type.  return a + (b as any); // T+T cannot be determined; two generics cannot perform data operations.}let r1 = sum(1, 2);let r2 = sum('1', '2');// Knowing conditional operators allows us to understand TypeScript's compatibility and type hierarchy.// Type Hierarchy// 1. Primitive Types// 2. Composite Types// 3. Function Types// 4. Class Types// Compatibility: allows assigning a value to another value.// Type Hierarchy: lower levels can be assigned to higher levels.

Type Hierarchy Compatibility

// Type Hierarchy// 1. Primitive Types// 2. Composite Types// 3. Function Types// 4. Class Types// Knowing conditional operators allows us to understand TypeScript's compatibility and type hierarchy.// Compatibility: means a value can be assigned to another value.// Type Hierarchy: lower levels can be assigned to higher levels.// Theory: who is the parent type, who is the child type.type R1 = 'abc' extends string ? true : false;type R2 = 123 extends number ? true : false;type R3 = true extends boolean ? true : false;// In practice: 'abc' is a string type, 123 is a number type, true is a boolean type.let r1: string = 'abc';let r2: number = 123;type R4 = 'a' extends 'a' | 'b' | 'c' ? true : false;type R5 = 1 extends 1 | 2 | 3 ? true : false;type R6 = true extends true | false ? true : false;//// Literal types can be assigned to literal union types.let r4: 'a' | 'b' | 'c' = 'a';// Wrapper types can be assigned to primitive types.// let r5: string = new String('abc');// Wrapper Typestype R7 = string extends String ? true : false;type R8 = String extends string ? true : false;type R9 = number extends Number ? true : false;type R10 = Number extends number ? true : false;type R11 = boolean extends Boolean ? true : false;type R12 = Boolean extends boolean ? true : false;type R13 = Object extends any ? true : false;type R14 = Object extends unknown ? true : false;type R15 = never extends 'abc' ? true : false;// never is the smallest type.// Literal types can be assigned to literal union types.// Literal types can be assigned to primitive types.// Primitive types are subtypes of wrapper types.// any and unknown are the largest types.// never < literal type < literal union type < primitive type < wrapper type < Object < any unknowntype R16 = any extends string ? true : false; // The union type of true and false is boolean.// For the any type, it always returns a union type of success and failure.// Lower types can be assigned to higher types.// Structurally, an intersection type can be assigned to the pre-intersection type.// By default in TS, Object and object are the same size: Object extends object? true:false. The reverse is not true.// If it has more properties structurally, it can be assigned to you.// In terms of level, lower levels can be assigned to higher levels.export {};

Utility Types

When normally judging types, you can use A extends B
Conditional type distribution (distribution feature is enabled by default)

  1. Type A is passed in through a generic.
  2. If type A is a union type, it will be distributed.
  3. The generic parameter A must be "naked" (not A & {}) to have type distribution.
// Application of conditional types in functions
interface Bird {
  fly: () => void;
}
interface Sky {
  name: '天空';
}
interface Fish {
  swim: () => void;
}
interface Water {
  name: '水';
}

type Condition = Fish | Bird extends Fish ? 'success' : 'fail'; // Type inferred as 'fail' (no distribution)
type Condition2<T> = T extends Fish ? 'success' : 'fail'; // Type inferred as 'success' (distribution)

type C3 = Condition2<Fish>; // success
// Distribution means comparing one by one.
// Condition2<Bird> fail
// Condition2<Fish> success
type C4 = Condition2<Bird | Fish>; // success | fail
// By default, sometimes we need to disable this distribution capability, which can lead to inaccurate judgments.
// How to eliminate this distribution type T & {}
type NoDistribute<T> = T & {};
// It's not a naked type; &{} loses the distribution capability. You can also add an array.
type Condition5<T, U> = [T] extends [U] ? true : false;
type R5 = Condition5<1 | 2, 1>; // false

// T & {} can eliminate distribution capability. Nothing happens; it's just to create a new type for T.
type IsNever<T> = T extends never ? true : false;
type R6 = IsNever<never>; // never directly returns never. Wrapping it in an array makes it true.

//  Utility Types
// 1. Extract
type Extract<T, U> = T extends U ? T : never;
type R7 = Extract<1 | 2, 1>; // 1 (distribution judgment)
// 2. Exclude
type Exclude<T, U> = T extends U ? never : T;
type R8 = Exclude<1 | 2, 1>; // 2 (distribution judgment)

// 3. NonNullable
type NonNullable<T> = T extends null | undefined ? never : T;
type R9 = NonNullable<string | null>; // string

// 4. ReturnType (infer inference can infer a part of the return type in conditional types)
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
type R10 = ReturnType<() => string>; // string

// 5. Parameters
type Parameters<T> = T extends (...args: infer P) => any ? P : never;
type R11 = Parameters<(name: string, age: number) => string>; // [string, number]

// 6. ConstructorParameters
type ConstructorParameters<T> = T extends new (...args: infer P) => any
  ? P
  : never;
type R12 = ConstructorParameters<Error>; // [message?: string | undefined]

// 7. Partial
type Partial<T> = {
  [P in keyof T]?: T[P];
};
type R13 = Partial<{ name: string; age: number }>; // { name?: string; age?: number }


// Application of conditional types in arrays
type Swap<T> = T extends [infer A, infer B] ? [B, A] : T;
type R1 = Swap<['jw', 30]>; // 30 'jw'

// Application of conditional types in functions
type IsEqual<A, B> = A extends B ? (B extends A ? true : false) : false;
type R2 = IsEqual<1, 2>; // false
type R3 = IsEqual<1, 1>; // true

export {};
type Swap<T> = T extends [infer A, infer B] ? [B, A] : T;
type R1 = Swap<['jw', 30]>; // 30 'jw'
// Swap head and tail
type SwapHeadTail<T> = T extends [infer A, ...infer B, infer C]
  ? [C, ...B, A]
  : T;
type R2 = SwapHeadTail<[1, 2, 3, 4, 5]>; // [5, 2, 3, 4, 1]

// Application of conditional types in strings
type IsString<T> = T extends string ? true : false;
type R14 = IsString<'jw'>; // true

// Application of conditional types in function overloading
type IsFunction<T> = T extends (...args: any[]) => any ? true : false;
type R15 = IsFunction<() => void>; // true

// Promise recursion
type PromiseReturnType<T> = T extends Promise<infer P>
  ? PromiseReturnType<P>
  : T;

function getVal(): Promise<number> {
  return new Promise((resolve) => {
    resolve(100);
  });
}
type R16 = PromiseReturnType<ReturnType<typeof getVal>>; // true
// Achieve recursive inference through infer
// Convert tuple to union type [number,boolean,string] => number | boolean | string
type TupleToUnion<T> = T extends [infer A, ...infer B]
  ? A | TupleToUnion<B>
  : T;
type R17 = TupleToUnion<[number, boolean, string]>; // number | boolean | string

// Convert union type to tuple type
type UnionToTuple<T> = T extends infer P ? [P] : never;
type R18 = UnionToTuple<number | boolean | string>; // [number] | [boolean] | [string]

// Refactor type structure: Partial, Required, Readonly, Pick, Omit, Record

interface Person {
  name: string;
  age: number;
  gender: string;
}
let person: Partial<Person> = {
  name: 'jw',
};

function mixin<T, U>(a: T, b: U): T & U {
  return { ...a, ...b };
}
let p = mixin({ name: 'jw' }, { age: 100 });
let x = mixin(
  { name: 'jiangwen', age: 30, c: 3 },
  { name: 123, age: 30, b: 2 }
);
// An intersection type appeared, not merged as intended, e.g., retaining the latter content.
// In this case, apply removing properties of B from A.
function mixin2<T, U>(a: T, b: U): Omit<T, keyof U> & U {
  return { ...a, ...b };
}
let y = mixin2(
  { name: 'jiangwen', age: 30, c: 3 },
  { name: 123, age: 30, b: 2 }
);

type nameType = (typeof y)['name'];

// Preserve the desired key-->value structure (some mapped types)
type Record<K extends keyof any, T> = {
  [P in K]: T;
};
type R19 = Record<'a' | 'b', string>; // { a: string; b: string }

export {};

Compatibility

// Duck typing, structural type checking
// Subtypes can be assigned to supertypes. From a structural perspective, TypeScript compares not the name of the type, but the properties and methods in its structure.
// Primitive type compatibility
let obj = {
  toString: () => '123',
};
let str: string = '234';

obj = str; // From a safety perspective, if I satisfy all the properties you need, but have other properties, then I am not compatible.
// 2. Interface compatibility
interface Person {
  name: string;
  age: number;
}
interface Animal {
  name: string;
  age: number;
  address: string;
}
let p: Person = { name: 'jw', age: 100 };
let a: Animal = { name: 'jw', age: 100, address: 'beijing' };

p = a; // From a safety perspective, if I satisfy all the properties you need, but have other properties, then I am not compatible.
// 3. Function compatibility
type Func = (a: number, b: number) => void;
let f1: Func = (a, b) => {};
let f2: Func = (a, b) => {}; // Parameters can only be fewer, not more (e.g., when we use forEach

f1 = f2; // From a safety perspective, if I satisfy all the properties you need, but have other parameters, then I am not compatible.

// 4. Class compatibility
class A {
  constructor(public name: string) {}
}
class B {
  constructor(public name: string, public age: number) {}
}
let a1 = new A('jw');
let b1 = new B('jw', 100);

a1 = b1; // From a safety perspective, if I satisfy all the properties you need, but have other properties, then I am not compatible.

// Function contravariance and covariance: function parameters are contravariant, return values are covariant.
class Parent {
  house() {}
}
class Child extends Parent {
  car() {
    console.log('child car');
  }
}
class GrandSon extends Child {
  money() {
    console.log('grandson money');
  }
}

function fn(callback: (instance: Child) => Child) {
  let child = new Child();
  let ins = callback(child);
  return ins;
}
// Why can the assigned function be of type Parent but not GrandSon? Internally, a Child type is passed, and when this instance is obtained, properties inaccessible to Child cannot be accessed.
fn((instance: Child) => {
  return new Child();
});

// The return value should be a subtype of the return type (viewed from different perspectives).
fn((instance: Child) => {
  return new GrandSon();
});
// For function compatibility, the number of parameters should be fewer, the passed type can be a supertype, and the return value can be a subtype.
// Derivation formula
type Arg<T> = (arg: T) => void;
type Return<T> = (arg: any) => T;
type ArgType = Arg<Parent> extends Arg<Child> ? true : false; // Contravariance
type ReturnType = Return<GrandSon> extends Return<Child> ? true : false; // Covariance

// Therefore, function parameters are contravariant, and return values are covariant.

// Enums do not have compatibility.
// When TypeScript compares type structures, it compares properties and methods. If all properties and methods match, then they are compatible.

// Achieve nominal typing through intersection types.
type Normal<T, K extends string> = T & { __type__: K };
type BTC = Normal<number, 'BTC'>;
type ETH = Normal<number, 'ETH'>;
type USDT = Normal<number, 'USDT'>;

let btc: BTC = 100 as BTC;
let eth: ETH = 200 as ETH;
let usdt: USDT = 300 as USDT;

function getVal(val: BTC) {
  return val.valueOf();
}

getVal(btc); // Passing other types will result in an error.

export {};

主题测试文章,只做测试使用。发布者:Walker,转转请注明出处:https://walker-learn.xyz/archives/4410

(0)
Walker的头像Walker
上一篇 Nov 25, 2025 05:00
下一篇 Nov 25, 2025 03:00

Related Posts

  • TS Everest 003 [Study Notes]

    Decorator // Decorator // Can only be used in classes (on the class itself, or class members) // Decorators, class decorators, property decorators, accessor decorators, parameter decorators //
    1. Type Decorator: Used to extend a class, or can also return a subclass. //
    First, `experimentalDecorators` must be enabled in `tsconfig.json`. `const classDecorator1 =

    Personal Mar 27, 2025
    1.5K00
  • Go Engineer System Course 004 [Study Notes]

    Requirements Analysis Backend Management System Product Management Product List Product Categories Brand Management Brand Categories Order Management Order List User Information Management User List User Addresses User Messages Carousel Management E-commerce System Login Page Homepage Product Search Product Category Navigation Carousel Display Recommended Products Display Product Details Page Product Image Display Product Description Product Specification Selection Add to Cart Shopping Cart Product List Quantity Adjustment Delete Product Checkout Function User Center Order Center My...

    Nov 25, 2025
    31000
  • In-depth Understanding of ES6 001 [Study Notes]

    Block-Level Scope Binding
    Previously, `var` variable declarations, regardless of where they were declared, were considered to be declared at the top of their scope. Since functions are first-class citizens, the typical order was `function functionName()`, followed by `var variable`.

    Block-Level Declarations
    Block-level declarations are used to declare variables that cannot be accessed outside the scope of a specified block. Block-level scope exists in:
    - Inside functions
    - Within blocks (the region between `{` and `}`)

    Temporal Dead Zone
    When the JavaScript engine scans code and finds variable declarations, it either hoists them to the top of the scope...

    Personal Mar 8, 2025
    1.9K00
  • Node: Demystified (Shengsi Garden Education) 001 [Learning Notes]

    Node.js: Understanding it from an Asynchronous Programming Paradigm
    Node.js's Positioning and Core Philosophy
    Based on the V8 engine + libuv event-driven library, it brings JavaScript from the browser to the server side. It uses a single-threaded event loop to handle I/O, maximizing CPU time slices while waiting for I/O, making it particularly suitable for high-concurrency, I/O-intensive scenarios. "Don't block the main thread" is its design philosophy: try to offload time-consuming operations to the kernel or a thread pool, and callback results...

    Personal Nov 24, 2025
    40700
  • Go Engineer's Comprehensive Course 017: Learning Notes

    Introduction to Rate Limiting, Circuit Breaking, and Degradation (with Sentinel Practical Application)
    Based on the key video points from Chapter 3 (3-1 to 3-9) of the courseware, this guide compiles a service protection introduction for beginners, helping them understand "why rate limiting, circuit breaking, and degradation are needed," and how to quickly get started with Sentinel.
    Learning Path at a Glance
    3-1 Understanding Service Avalanche and the Background of Rate Limiting, Circuit Breaking, and Degradation
    3-2 Comparing Sentinel and Hystrix to clarify technology selection
    3-3 Sen...

    Personal Nov 25, 2025
    28200
EN
简体中文 繁體中文 English