我试图以一种可以定义名称,输入和返回类型,然后从中央函数访问它们的方式来编写函数。但是,这样做时,我会丢失键入信息。
如何构建没有硬编码值类型的系统?
import * as fs from 'fs';
import { promisify } from 'util';
import * as lodash from 'lodash';
const libs = []
export enum Types {
number,
numbers,
string,
buffer,
}
export enum Actions {
add,
subtract,
readFile,
}
libs.push({
action: Actions.add,
from: Types.numbers,
get: Types.number,
fn: (...n: number[]): number => n.reduce((a, b) => a + b, 0),
})
libs.push({
action: Actions.subtract,
from: Types.numbers,
get: Types.number,
fn: (...n: number[]): number => n.reduce((a, b) => a - b, 0),
})
libs.push({
action: Actions.readFile,
from: Types.string,
get: Types.string,
fn: async (s:string): Promise<string> => promisify(fs.readFile)(s, 'UTF8'),
})
libs.push({
action: Actions.readFile,
from: Types.string,
get: Types.buffer,
fn: async (s:string): Promise<Buffer> => promisify(fs.readFile)(s),
})
const library = (a: Actions, from: Types, get: Types, lib) => {
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;
}
const { readFile } = Actions;
const { string: s } = Types;
const x = library(readFile, s, s, libs)
x('./tres.ts').then(console.log)
如何保留x
的键入信息?
答案 0 :(得分:2)
我们需要在libs
中保留数组中实际项目的类型。最简单的方法是借助一个额外的函数,该函数将根据数组中的实际项目(包括libs
和Actions
的文字类型)来推断Types
的类型。使用)。
使用此信息,我们可以键入library
函数以从具有相同libs
,action
和get
的{{1}}中提取函数的类型作为传入的类型:
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