Extending built-in types in Typescript
Asked Answered
I

1

5

I have the following structure:

project
 |- types
    |- global.d.ts
    |- string.d.ts
    |- wdio.d.ts
 |- src
    |- Models
    |- Resources
    |- Components
    |- Extensions
       |- string.ts
    |- ...
 |- tsconfig.json
 |- wdio.conf.js

I try to extend the string's prototype with a function. I tried so far a lot of way, I found on several sites. But either the tsc gives me error, or the PHPStorm shows error message.

// types/string.d.ts
declare interface String {
    myCustomFn(text : string) : string;
}

// src/Extensions/string.ts
String.prototype.myCustomFn = function(text : string) : string {
    // ... Logic

    return 'myCustomFn';
};

// tsconfig.json
...
    "typeRoots": ["./types/"],

    "include": [
      "./src/**/*.ts",
      "./types"
    ]
...

// wdio.conf.js
...
before: function (capabilities, specs) {
    require('ts-node').register({ files: true });

    require('../extensions/String');
},
...

I added the augmentation for the String class to the d.ts file. Then I define the body of the function in a separate file. When I implement it in the src/Extensions/string.ts file, the tsc command gives no error message, BUT the PHPStorm shows the following error:

TS2339: Property 'myCustomFn' does not exist on type 'String'.

Moreover, anywhere in the code the auto-completition shows my method, and even the code can be executed, and uses the myCustomFn function.

Questions:

  • Is this just an error of the IDE?
  • Am I doing something wrong or should the way, how the String class is being extended be in different way?
Implacable answered 7/10, 2019 at 8:33 Comment(0)
M
7

Full working example on Github

Place your String interface extension in a random .d.ts file:

interface String {
  myCustomFn(text : string) : string;
}

Add another file extension.ts where you add the prototype:

String.prototype.myCustomFn = function(text : string) : string {
  return 'myCustomFn';
};

Then import the extension.ts file into your root index.ts file:

import './extension';

Now you can add your String().myCustomFn(text: string); everywhere you want.


P.S. it is important that you include the .d.ts file to your compilation files. The typeRoots property is not necessary.

tsconfig.json:

{
  "compilerOptions": {
    "outDir": "dist"
  },
  "include": [
    "src",
    "types" // here is the .d.ts file
  ],
  "exclude": [
    "**/node_modules"
  ]
}

Miletus answered 7/10, 2019 at 8:57 Comment(3)
That's the thing. I doing exactly the same thing. There is a .d.ts file - in which I extend the prototype with a function. In another file I write the implementation of that function. On the initialisation of the code I require the file. I provided more info about my files. Btw: this is a webdriver-io project. In the before callback I require the file. And the code runs. What I don't understand why does the IDE marks it as an error.Mcginty
If your .d.ts file imports anything, TypeScript won't consider the module to be "in the global scope" anymore, as it's now a "module". To fix this, you'll need to put your interface inside of declare global { ... }. See stackoverflow.com/a/56984941 for more context.Conservative
What if the type you extend has a generic? And what if you want to use the "base" object in the function?Tricia

© 2022 - 2024 — McMap. All rights reserved.