ΠΠ°ΠΊ ΡΠΈΠΏΠΈΠ·ΠΈΡΠΎΠ²Π°ΡΡ ΡΡΠ½ΠΊΡΠΈΡ-ΡΠ°Π±ΡΠΈΠΊΡ Ρ generic?
ΠΠΎΠ»Π½ΡΠΉ ΠΎΡΠ²Π΅Ρ
Π‘Π°ΠΌΠ°Ρ ΠΏΡΠΎΡΡΠ°Ρ ΡΠ°Π±ΡΠΈΠΊΠ° ΠΏΡΠΈΠ½ΠΈΠΌΠ°Π΅Ρ ΠΊΠ»Π°ΡΡ ΠΈ Π²ΠΎΠ·Π²ΡΠ°ΡΠ°Π΅Ρ Π΅Π³ΠΎ ΡΠΊΠ·Π΅ΠΌΠΏΠ»ΡΡ:
// ΠΠ°Π·ΠΎΠ²Π°Ρ ΡΠ°Π±ΡΠΈΠΊΠ° β ΠΏΡΠΈΠ½ΠΈΠΌΠ°Π΅Ρ ΠΊΠΎΠ½ΡΡΡΡΠΊΡΠΎΡ Π±Π΅Π· Π°ΡΠ³ΡΠΌΠ΅Π½ΡΠΎΠ²
function create<T>(Ctor: new () => T): T {
return new Ctor();
}
class User {
name = "Alice";
}
class Product {
title = "Laptop";
}
const user = create(User); // ΡΠΈΠΏ: User
const product = create(Product); // ΡΠΈΠΏ: Product
console.log(user.name); // "Alice"
console.log(product.title); // "Laptop"
ΠΠΎΠ³Π΄Π° ΠΊΠΎΠ½ΡΡΡΡΠΊΡΠΎΡΡ Π½ΡΠΆΠ½Ρ Π°ΡΠ³ΡΠΌΠ΅Π½ΡΡ, generic ΡΠ°ΡΡΠΈΡΡΠ΅ΡΡΡ:
// Π€Π°Π±ΡΠΈΠΊΠ° Ρ Π°ΡΠ³ΡΠΌΠ΅Π½ΡΠ°ΠΌΠΈ ΠΊΠΎΠ½ΡΡΡΡΠΊΡΠΎΡΠ°
function createWith<T, A extends unknown[]>(
Ctor: new (...args: A) => T,
...args: A
): T {
return new Ctor(...args);
}
class Order {
constructor(
public id: number,
public amount: number
) {}
}
const order = createWith(Order, 1, 99.99); // ΡΠΈΠΏ: Order
// createWith(Order, "abc"); // ΠΎΡΠΈΠ±ΠΊΠ°: string Π½Π΅ ΠΏΠΎΠ΄Ρ
ΠΎΠ΄ΠΈΡ Π΄Π»Ρ number
Π€Π°Π±ΡΠΈΠΊΠ° Ρ ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΠ΅ΠΉ Π²ΠΌΠ΅ΡΡΠΎ ΠΊΠΎΠ½ΡΡΡΡΠΊΡΠΎΡΠ°:
// Π€Π°Π±ΡΠΈΠΊΠ° ΡΠ΅ΡΠ΅Π· ΠΊΠΎΠ½ΡΠΈΠ³-ΠΎΠ±ΡΠ΅ΠΊΡ
interface WidgetConfig<T> {
type: string;
build: (config: Record<string, unknown>) => T;
}
function createWidget<T>(widget: WidgetConfig<T>, config: Record<string, unknown>): T {
return widget.build(config);
}
interface Chart {
render(): void;
}
const chartFactory: WidgetConfig<Chart> = {
type: "chart",
build: (config) => ({
render() {
console.log("Rendering chart", config);
},
}),
};
const chart = createWidget(chartFactory, { data: [1, 2, 3] }); // ΡΠΈΠΏ: Chart
chart.render();
Π€Π°Π±ΡΠΈΠΊΠ°-ΡΠ΅Π΅ΡΡΡ Π΄Π»Ρ Π½Π΅ΡΠΊΠΎΠ»ΡΠΊΠΈΡ ΠΊΠ»Π°ΡΡΠΎΠ²:
// Π Π΅Π΅ΡΡΡ ΡΠ°Π±ΡΠΈΠΊ Ρ discriminated map
interface ShapeMap {
circle: { radius: number };
rect: { width: number; height: number };
}
function createShape<K extends keyof ShapeMap>(
kind: K,
params: ShapeMap[K]
): ShapeMap[K] & { kind: K } {
return { ...params, kind };
}
const circle = createShape("circle", { radius: 10 }); // ΡΠΈΠΏ: { radius: number } & { kind: "circle" }
const rect = createShape("rect", { width: 5, height: 3 }); // ΡΠΈΠΏ: { width: number; height: number } & { kind: "rect" }
// createShape("circle", { width: 5 }); // ΠΎΡΠΈΠ±ΠΊΠ°: width Π½Π΅ ΡΡΡΠ΅ΡΡΠ²ΡΠ΅Ρ Π² { radius: number }
Π Π΅Π°Π»ΡΠ½ΡΠ΅ ΠΊΠ΅ΠΉΡΡ
DI-ΠΊΠΎΠ½ΡΠ΅ΠΉΠ½Π΅Ρ Ρ ΡΠΈΠΏΠΎΠ±Π΅Π·ΠΎΠΏΠ°ΡΠ½ΡΠΌ resolve:
class Container {
private registry = new Map<string, new (...args: never[]) => unknown>();
register<T>(key: string, Ctor: new () => T): void {
this.registry.set(key, Ctor as new (...args: never[]) => unknown);
}
resolve<T>(key: string): T {
const Ctor = this.registry.get(key);
if (!Ctor) throw new Error(`Not registered: ${key}`);
return new Ctor() as T;
}
}
Π’ΠΈΠΏΠΈΡΠ½Π°Ρ ΠΎΡΠΈΠ±ΠΊΠ° β ΠΏΠΎΡΠ΅ΡΡ ΡΠΈΠΏΠ° ΠΈΠ·-Π·Π° Function:
// β ΠΠ΅Ρ ΡΠΈΠΏΠΈΠ·Π°ΡΠΈΠΈ Π²ΠΎΠ·Π²ΡΠ°ΡΠ°Π΅ΠΌΠΎΠ³ΠΎ Π·Π½Π°ΡΠ΅Π½ΠΈΡ
function badFactory(Ctor: Function) {
return Ctor();
}
// β
Generic ΡΠΎΡ
ΡΠ°Π½ΡΠ΅Ρ ΡΠΈΠΏ
function goodFactory<T>(Ctor: new () => T): T {
return new Ctor();
}
Π Π΅Π·ΡΠΌΠ΅
Π€Π°Π±ΡΠΈΠΊΠ° Ρ generic ΡΡΡΠΎΠΈΡΡΡ Π²ΠΎΠΊΡΡΠ³ ΠΊΠΎΠ½ΡΡΡΡΠΊΡΠΎΡΠ½ΠΎΠΉ ΡΠΈΠ³Π½Π°ΡΡΡΡ new (...args: A) => T. ΠΡΠΎ ΠΏΠΎΠ·Π²ΠΎΠ»ΡΠ΅Ρ TypeScript Π²ΡΠ²Π΅ΡΡΠΈ ΡΠΈΠΏ Π²ΠΎΠ·Π²ΡΠ°ΡΠ°Π΅ΠΌΠΎΠ³ΠΎ ΡΠΊΠ·Π΅ΠΌΠΏΠ»ΡΡΠ° Π°Π²ΡΠΎΠΌΠ°ΡΠΈΡΠ΅ΡΠΊΠΈ. ΠΠ»Ρ ΡΠ°Π±ΡΠΈΠΊ Π½Π° ΠΎΡΠ½ΠΎΠ²Π΅ ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΠΈ ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠΉΡΠ΅ mapped types ΠΈΠ»ΠΈ discriminated map, ΡΡΠΎΠ±Ρ ΡΠ²ΡΠ·Π°ΡΡ ΠΊΠ»ΡΡ Ρ ΠΊΠΎΠ½ΠΊΡΠ΅ΡΠ½ΡΠΌ ΡΠΈΠΏΠΎΠΌ ΡΠ΅Π·ΡΠ»ΡΡΠ°ΡΠ°.