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
上一篇 Mar 10, 2026 00:00
下一篇 Mar 8, 2026 15:40

Related Posts

  • Go Engineer Structured Course 011 [Learning Notes]

    Inverted Index for Queries
    1. What is an Inverted Index?
    An Inverted Index is a data structure used to quickly find documents containing specific terms. It is one of the core technologies of search engines.
    1.1 Basic Concepts
    Forward Index: Document ID → Document Content (list of terms)
    Inverted Index: Term → List of Document IDs containing the term
    1.2 Why is it called "Inverted"?
    An inverted index reverses the traditional relationship of "which terms a document contains" to "in which documents a term appears...

    Personal Nov 25, 2025
    26700
  • In-depth Understanding of ES6 007 [Study Notes]

    Set and Map Collections. In JS, there is an `in` operator that can determine if a property exists in an object without needing to read the object's value, returning true if it exists. However, the `in` operator also checks the object's prototype chain, so using this method is only relatively safe when the object's prototype is null. Set Collection: `let set = new Set()` `set.add(5)` `set.add("5")` `console.log(s…`

    Personal Mar 8, 2025
    1.3K00
  • In-depth Understanding of ES6 013 [Study Notes]

    Code Encapsulation with Modules

    JavaScript loads code using a "share everything" approach to loading code, which is one of the most error-prone and confusing aspects of the language. Other languages use concepts like packages to define code scope. Before ES6, everything defined in every JavaScript file within an application shared a single global scope. As web applications became more complex and the amount of JavaScript code grew, this practice led to issues such as naming conflicts and security concerns. One of ES6's goals was to address the scoping issue…

    Personal Mar 8, 2025
    1.2K00
  • In-depth Understanding of ES6 004 [Study Notes]

    Extending object functionality
    Ordinary objects: objects that possess all default internal behaviors of a JavaScript object.
    Exotic objects: objects that possess certain internal behaviors that deviate from the default.
    Standard objects: objects defined in the ES6 specification, such as Array/Date.
    Built-in objects: objects that exist in the JavaScript execution environment when the script begins execution; all standard objects are built-in objects.
    Object literal syntax extensions:
    Shorthand for property initializers: when an object's property has the same name as a local variable, there's no need to write the colon and value.
    Shorthand syntax for object methods...

    Personal Mar 8, 2025
    1.3K00
  • Go Engineer System Course 007 [Study Notes]

    Goods Microservice Entity Structure Description This module contains the following core entities: Goods (Goods) Goods Category (Category) Brand (Brands) Carousel (Banner) Goods Category Brand (GoodsCategoryBrand) 1. Goods (Goods) Describes the product information actually displayed and sold on the platform. Field Description Field Name Type Description name String Product name, required brand Pointer …

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