我们需要保护自己
libs
数组中实际项的类型。最简单的方法是借助一个额外的函数来推断
自由基
基于数组中的实际项(包括where的文本类型
Actions
和
Types
根据这些信息,我们可以键入
library
要从中提取函数类型的函数
自由基
那是一样的
action
,
get
和
from
作为传入类型:
import * as fs from 'fs';
import { promisify } from 'util';
import * as lodash from 'lodash';
export enum Types {
number,
numbers,
string,
buffer,
}
export enum Actions {
add,
subtract,
readFile,
}
function makeLib<T extends Array<{action : A, from: F, get: G, fn: (...a: any[])=> any}>, A extends Actions, F extends Types, G extends Types>(...a:T){
return a;
}
const libs = makeLib({
action: Actions.add,
from: Types.numbers,
get: Types.number,
fn: (...n: number[]): number => n.reduce((a, b) => a + b, 0),
}, {
action: Actions.subtract,
from: Types.numbers,
get: Types.number,
fn: (...n: number[]): number => n.reduce((a, b) => a - b, 0),
}, {
action: Actions.readFile,
from: Types.string,
get: Types.string,
fn: async (s:string): Promise<string> => promisify(fs.readFile)(s, 'UTF8'),
}, {
action: Actions.readFile,
from: Types.string,
get: Types.buffer,
fn: async (s:string): Promise<Buffer> => promisify(fs.readFile)(s),
})
const library = <T extends Array<{action : Actions, from: Types, get: Types, fn: (...a: any[])=> any}>, A extends Actions, F extends Types, G extends Types>(a: A, from: F, get: G, lib: T) => {
const found = lodash.find(lib, fn => {
return (
lodash.isEqual(fn.from, from) &&
lodash.isEqual(fn.get, get)
);
});
if (!found) throw new Error('no conversion');
return found.fn as Extract<T[number], {action : A, from: F, get: G }>['fn'];
}
const { readFile } = Actions;
const { string: s } = Types;
const x = library(readFile, s, s, libs) // x is (s: string) => Promise<string
x('./tres.ts').then(console.log)
const x2 = library(Actions.subtract, Types.string, Types.string, libs) // never
const x3 = library(Actions.subtract, Types.numbers, Types.number, libs) // (...n: number[]) => number
strings
而不是枚举:
function makeLib<T extends Array<{action : V, from: V, get: V, fn: (...a: any[])=> any}>, V extends string>(...a:T){
return a;
}
const libs = makeLib({
action: "add",
from: "numbers",
get: "number",
fn: (...n: number[]): number => n.reduce((a, b) => a + b, 0),
}, {
action: "subtract",
from: "numbers",
get: "number",
fn: (...n: number[]): number | null => n.reduce((a, b) => a - b, 0),
}, {
action: "readFile",
from: "string",
get: "string",
fn: async (s:string): Promise<string> => promisify(fs.readFile)(s, 'UTF8'),
}, {
action: "readFile",
from: "string",
get: "buffer",
fn: async (s:string): Promise<Buffer> => promisify(fs.readFile)(s),
})
const library = <T extends Array<{action : string, from: string, get: string, fn: (...a: any[])=> any}>,
A extends T[number]['action'], F extends T[number]['from'], G extends T[number]['get']>(a: A, from: F, get: G, lib: T) => {
const found = lodash.find(lib, fn => {
return (
lodash.isEqual(fn.from, from) &&
lodash.isEqual(fn.get, get)
);
});
if (!found) throw new Error('no conversion');
return found.fn as Extract<T[number], {action : A, from: F, get: G }>['fn'];
}
const { readFile } = Actions;
const { string: s } = Types;
const x = library("readFile", "string", "string", libs) // x is (s: string) => Promise<string
x('./tres.ts').then(console.log)
const x2 = library("subtract", "string", "string", libs) // never
const x3 = library("subtract", "numbers", "number", libs) // (...n: number[]) => number