具有定义类型的函数库

时间:2019-02-05 06:17:15

标签: typescript

我试图以一种可以定义名称,输入和返回类型,然后从中央函数访问它们的方式来编写函数。但是,这样做时,我会丢失键入信息。

如何构建没有硬编码值类型的系统?

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的键入信息?

1 个答案:

答案 0 :(得分:2)

我们需要在libs中保留数组中实际项目的类型。最简单的方法是借助一个额外的函数,该函数将根据数组中的实际项目(包括libsActions的文字类型)来推断Types的类型。使用)。

使用此信息,我们可以键入library函数以从具有相同libsactionget的{​​{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