打字稿用函数创建自己的类型

Posted

技术标签:

【中文标题】打字稿用函数创建自己的类型【英文标题】:Typescript creating own types with functions 【发布时间】:2020-06-13 10:12:45 【问题描述】:

我正在尝试创建自己的类型,我可以使用 dot syntax 在其上调用函数。

例如:

let myOwnType: myByteType = 123211345;
myOwnType.toHumanReadable(2);

我想归档相同的行为,例如数字、数组等。 我不想使用调用签名或构造函数签名来创建我的类型

所以在查看了 typescript 库后,我看到了这个数字界面:

interface Number 
    /**
     * Returns a string representation of an object.
     * @param radix Specifies a radix for converting numeric values to strings. This value is only used for numbers.
     */
    toString(radix?: number): string;

    /**
     * Returns a string representing a number in fixed-point notation.
     * @param fractionDigits Number of digits after the decimal point. Must be in the range 0 - 20, inclusive.
     */
    toFixed(fractionDigits?: number): string;

    /**
     * Returns a string containing a number represented in exponential notation.
     * @param fractionDigits Number of digits after the decimal point. Must be in the range 0 - 20, inclusive.
     */
    toExponential(fractionDigits?: number): string;

    /**
     * Returns a string containing a number represented either in exponential or fixed-point notation with a specified number of digits.
     * @param precision Number of significant digits. Must be in the range 1 - 21, inclusive.
     */
    toPrecision(precision?: number): string;

    /** Returns the primitive value of the specified object. */
    valueOf(): number;

问题是我找不到函数体的定义位置和方式。我在想一定有一个实现接口的类或类似的东西。

我想出了这样的尝试:

interface Bytes 
  toHumanReadable(decimals: number): bytesToHumanReadable;
  bytesAs(unit: string): string;



function bytesToHumanReadable (decimals: number) 
  // convert bytes to GB, TB etc..

我如何创建自己的类型并像使用普通类型一样使用它们? 我知道接口也不起作用,但这是我的第一个猜测。

【问题讨论】:

您可能想使用class,而不是interface(请参阅***.com/a/55505227/2358409) 【参考方案1】:

T.J.克劳德很好的答案,我对此进行了扩展

 interface myByteType extends Number 
      toHumanReadable?(): string;
     

    // Adding the implementation
    Object.defineProperty(Number.prototype, "toHumanReadable", 
      value() 
          return this.toString(16);
      ,
      enumerable: false, // This is the default, but just for emphasis...
      writable: true,
      configurable: true
    );

    let a: myByteType = 2124;
    a.toHumanReadable();

使用问号语法,您有一个解决方法,可以将您自己的类型与您定义的函数一起使用!

【讨论】:

【参考方案2】:

问题是我找不到函数体的定义位置和方式。

它们由 javascript 引擎定义,作为标准库的一部分。您可以在Number.prototype 上找到它们。它是这样工作的:当你尝试访问一个原始的属性(例如,someNumber.toString())时,JavaScript 引擎会在该原始对象类型的原型上查找属性(数字 => Number,字符串 => String 等)。如果您正在进行调用,它会调用该方法,传入原语(在严格模式下)或原语的对象包装器(在松散模式下)。

您无法定义自己的原始类型,只能在 JavaScript 规范级别完成。

您可以创建一个Byte 类并拥有它的实例,但它们将是对象,而不是原语。

class Byte 
    constructor(public value: number) 
    
    yourMethodHere() 
        // ...
    

您也可以向Number.prototype 添加方法,但最好不要在您可能与他人共享的任何库代码中这样做。 (在您自己的应用程序或页面中,这几乎没有问题,只需确保使用标准库的未来功能不太可能使用的名称。)

例如,这会将toHex 方法添加到数字:

// Adding it to the type
interface Number 
    toHex(): string;

// Adding the implementation
Object.defineProperty(Number.prototype, "toHex", 
    value() 
        return this.toString(16);
    ,
    enumerable: false, // This is the default, but just for emphasis...
    writable: true,
    configurable: true
);

Live example on the playground

【讨论】:

很好的解释。我真的不想在数字原型中添加一个方法,因为你已经提到了可能的麻烦。我将更多地了解 javascript 引擎本身,并可能使用接口和对象来代替。【参考方案3】:

bytesToHumanReadable 函数已经完成了一半,但你缺少一件关键的事情。

由于您想为数字添加新功能,您必须扩展基本的NumberConstructor 接口并自己实现该功能。让我们在实践中看看这个。

interface myByteType extends NumberConstructor = 
 toHumanReadable: (decimals: number): string

这只是为了满足 TypeScript 编译器。您仍然必须通过像这样扩展 Number 原型来自己实现该功能:

Number.prototype.toHumanReadable = function(decimals: number) 
 // convert bytes to GB, TB etc.. 

然后您可以对任何扩展myByteType 接口的变量使用toHumanReadable 函数,如下所示:

const secondsSinceOpened: myOwnType = 1334244200

secondsSinceOpened.toHumanReadable(2)

这个问题与this one 非常相似,因此您可以查看它以进一步阅读。

【讨论】:

通过赋值在内置原型上创建属性并不是最佳实践。确保该属性是不可枚举的(请参阅我的答案了解如何)。 上面给出了const secondsSinceOpened: myOwnType = 1334244200的类型错误(如果我用接口定义修复了各种错误并使类型名称匹配):“类型'1334244200'不可分配给类型' myOwnType'"(如果您尝试分配键入 number 的内容也会抱怨)。

以上是关于打字稿用函数创建自己的类型的主要内容,如果未能解决你的问题,请参考以下文章

使用打字稿创建自定义反应路由组件。类型上不存在属性“路径”... RouteProps

从类创建派生类型,但省略构造函数(打字稿)

打字稿中的重载函数类型

打字稿:函数参数的类型辅助

创建一个返回反应组件类的打字稿函数

来自键值对象的打字稿类型函数