如何使用 TypeScript 从 npm 扩展模块?

How to extend a module from npm using TypeScript?

我正在使用 joi and @types/joi with TypeScript. Joi has an extend 方法,该方法允许通过返回新实例来扩展 joi,而无需修改原始 joi 库。我用它创建了一个扩展实例。

为了创建此扩展实例的定义,我尝试 Module Augmentation 使用下面的代码 here 所述:

declare module 'joi' {
  // Add a new Schema type which has noChildren() method.
  interface CustomSchema extends ObjectSchema {
    noChildren(): this;
  }
}

但是,正如预期的那样,这通过扩充修改了原始定义。我想要的是为扩展实例创建定义,它继承了原始实例的所有内容而不修改它。

同样扩展Joi创建如下:

import * as Joi from 'joi';
const JoiExtended = Joi.extend({...some implementation...})
// How to export?
// export * from 'Joi' ---> In this case, original non-extended Joi is exported
// export default JoiExtended ---> Imported `Joi` reports: Cannot find namespace 'Joi'
  1. 如何创建扩展定义?
  2. 如何导出扩展Joi

P.S。我正在学习 TypeScript 并搜索了这个问题的答案,但找不到答案,可能是因为我不习惯 TypeScript 术语并搜索了错误的术语。

Joi.extend returns joi 模块的新实例,您可以为其使用导出的 Root 类型。

您需要创建一个扩展 Joi.Root 的接口和另一个扩展您正在扩展的 joi 基础类型的接口。您可以像导出任何其他对象一样简单地导出自定义 joi 实例。

下面是一个使用 extend() 上的 API Documentation 示例中的 round()dividable() 规则的示例。

import * as Joi from 'joi';

interface ExtendedNumberSchema extends Joi.NumberSchema {
    round(): this;
    dividable(num: number): this;
}

interface ExtendedJoi extends Joi.Root {
    number(): ExtendedNumberSchema;
}

const customJoi: ExtendedJoi = Joi.extend((joi) => ({
    base: joi.number(),
    name: 'number',
    language: {
        round: 'needs to be a rounded number', // Used below as 'number.round'
        dividable: 'needs to be dividable by {{q}}'
    },
    pre(value, state, options) {

        if (options.convert && this._flags.round) {
            return Math.round(value); // Change the value
        }

        return value; // Keep the value as it was
    },
    rules: [
        {
            name: 'round',
            setup(params) {

                this._flags.round = true; // Set a flag for later use
            },
            validate(params, value, state, options) {

                if (value % 1 !== 0) {
                    // Generate an error, state and options need to be passed
                    return this.createError('number.round', {v: value}, state, options);
                }

                return value; // Everything is OK
            }
        },
        {
            name: 'dividable',
            params: {
                q: joi.alternatives([joi.number().required(), joi.func().ref()])
            },
            validate(params, value, state, options) {

                if (value % params.q !== 0) {
                    // Generate an error, state and options need to be passed, q is used in the language
                    return this.createError('number.dividable', {v: value, q: params.q}, state, options);
                }

                return value; // Everything is OK
            }
        }
    ]
}));


const schema = {
    a: customJoi.number().round().dividable(3)
};

const result = customJoi.validate({a: 4.1}, schema); // will fail because 4 is no divisible by 3

console.log(result);

export = customJoi;