How can I extend third-party declaration files?
for example, I want to extend Context from #types/koa and add an extra field(resource) to it.
I tried this:
// global.d.ts
declare namespace koa {
interface Context {
resource: any;
}
}
But it doesn't work:
error TS2339: Property 'resource' does not exist on type 'Context'.
Update
a simplified version of my code which produces this error:
import {Context} from 'koa';
import User from './Models/User';
class Controller {
async list(ctx: Context) {
ctx.resources = await User.findAndCountAll();
ctx.body = ctx.resources.rows;
ctx.set('X-Total-Count', ctx.resources.count.toString());
ctx.status = 200;
}
}
typescript v2.4
// tsconfig.json
{
"compilerOptions": {
"target": "es6",
"module": "commonjs",
"moduleResolution": "node",
"noImplicitAny": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true
},
"exclude": [
"node_modules"
]
}
You have to use module augmentation as described here:
import { Context } from "koa";
declare module "koa" {
interface Context {
resource: any;
}
}
Related
I'm translating this javascript project (https://github.com/Legorin/satisfactory-calculator) to typescript.
I'm getting an error when I import the transpiled typescript file in a html script tag.
<script>
var handlers = {}
</script>
<script type="module">
import { Initilatizion } from "./init.js";
handlers.init = Initilatizion.init;
</script>
The error is: Uncaught SyntaxError: The requested module './init.js' does not provide an export named 'Initilatizion'
Here is my init.ts file
import { Belt } from "./belt";
import { Building } from "./building";
import { FactorySpecification } from "./factorySpecification";
import { Fragment } from "./fragment";
import { Item } from "./item";
import { Recipe } from "./recipe";
import { Settings } from "./settings";
// tslint:disable-next-line: no-var-requires
const d3 = require("../third_party/d3.min.js");
export class Initilatizion {
public static loadData(settings) {
const spec: FactorySpecification = FactorySpecification.getInstance();
d3.json("data/data.json").then((data) => {
const items = Item.getItems(data);
const recipes = Recipe.getRecipes(data, items);
const buildings = Building.getBuildings(data);
const belts = Belt.getBelts(data);
spec.setData(items, recipes, buildings, belts);
Settings.renderSettings(settings);
spec.updateSolution();
});
}
public static init() {
const settings = Fragment.loadSettings(window.location.hash);
this.loadData(settings);
}
}
Here is my tsconfig.json
{
"compilerOptions": {
"module": "commonjs",
"esModuleInterop": true,
"target": "es6",
"moduleResolution": "node",
"sourceMap": true,
"allowJs": true,
"outDir": "dist",
"sourceRoot": "src",
},
}
Where is my mistake?
You declared the wrong module type in your tsconfig.json!
By setting module to "commonjs" you tell typescript to generate a JavaScript file that uses the commonjs import/export syntax used by nodejs and others.
You however, use the "new" ES6-module import/export syntax in your HTML and therefore have to set module to "esnext" or "es6"!
Related documentation
Note: You shared an invalid tsconfig.json file, I fixed it below
Example tsconfig.json:
{
"compilerOptions": {
"module": "es6",
"esModuleInterop": true,
"target": "es6",
"moduleResolution": "node",
"sourceMap": true,
"allowJs": true,
"outDir": "dist",
"sourceRoot": "src"
},
}
In plain JS we can use import statements with data uri, e.g.:
import { number, fn } from 'data:text/javascript;charset=utf-8;base64,ZXhwb3J0IGNvbnN0IG51bWJlciA9IDQyOwpleHBvcnQgY29uc3QgZm4gPSAoKSA9PiAiSGVsbG8gd29ybGQiOw==';
Or dynamically:
import('data:text/javascript;charset=utf-8;base64,ZXhwb3J0IGNvbnN0IG51bWJlciA9IDQyOwpleHBvcnQgY29uc3QgZm4gPSAoKSA9PiAiSGVsbG8gd29ybGQiOw==')
.then(module => console.log(module));
However, putting the same code in a typescript file gets us a "Cannot find module" error.
My tsconfig.json is as follows:
{
"compilerOptions": {
"module": "esnext",
"lib": [
"esnext",
"es6",
"dom"
],
"moduleResolution": "node",
"outDir": "./build",
"noImplicitAny": false,
"removeComments": true,
"preserveConstEnums": true,
"sourceMap": true,
"target": "es6",
"jsx": "react"
},
"include": [
"src/**/*", "../addon/build/background.js"
],
"exclude": [
"node_modules",
"**/*.spec.ts"
]
}
Typescript docs on module resolution have no mention of data:... from what I see. Is there a way to get this to work in typescript?
See Wildcard module declarations in the Typescript handbook.
You could do something like this:
// types.d.ts
declare module "data:text/javascript;*" {
export const number: number;
export function fn(): string;
}
// index.ts
/// <reference path="types.d.ts"/>
import { number, fn } from 'data:text/javascript;charset=utf-8;base64,ZXhwb3J0IGNvbnN0IG51bWJlciA9IDQyOwpleHBvcnQgY29uc3QgZm4gPSAoKSA9PiAiSGVsbG8gd29ybGQiOw==';
This tells Typescript that an import matching the data:text/javascript; prefix will expose a number property (as a number type) and an fn property (which returns a string). Adjust the module declaration to fit your importer semantics as appropriate.
I have a declaration file written for extsting npm package, but seems like one method was not declared, I try to declare it, but get an error. Help me please.
structure of existing d.ts file:
declare module "mongoose" {
...
class Document {}
interface Document extends MongooseDocument, NodeJS.EventEmitter, ModelProperties {
increment(): this;
remove(fn?: (err: any, product: this) => void): Promise<this>;
...
}
}
I try to add to interface Document method deleteOne. My custom.d.ts:
declare module "mongoose" {
interface Document {
deleteOne(fn?: (err: any, product: this) => void): Promise<this>;
}
}
But still I get an error "Property 'deleteOne' does not exist on type".
Here is my tsconfig.json if you need:
{
"compilerOptions": {
"module": "commonjs",
"removeComments": true,
"esModuleInterop": true,
"moduleResolution": "node",
"allowJs": true,
"allowSyntheticDefaultImports": true,
"pretty": true,
"resolveJsonModule": true,
"sourceMap": true,
"target": "ES2018",
"outDir": "dist",
"baseUrl": ".",
"paths": {
"*": [
"node_modules/*"
]
}
},
"include": [
"src/**/*"
],
"exclude": [
"node_modules",
"dist",
"**/*.spec.ts"
]
}
My custom.d.ts file located in 'src/' dir.
OK! Now I know this is expected behavior of ts-node: https://github.com/TypeStrong/ts-node#missing-types
I have configured paths settings in tsconfig.json, and now everything is working:
"paths": {
"mongoose": ["src/custom.d.ts"],
"*": [
"node_modules/*"
]
}
defining the Mongoose interface
// types/mongoose/index.d.ts
declare module 'mongoose' {
namespace Mongoose {
export interface MyInterface {
name: number;
}
}
}
usage
// app.ts
import mongoose, { Mongoose } from 'mongoose';
const a: Mongoose.MyInterface = {
name: 122
};
I have also added "typeRoots": ["./node_modules/#types", "./server/types"], to my tsconfig file
does this help
I have a file named constants.ts which contains constants related to my React TypeScript application.
The file looks like this:
// The error for when errors are bad.
const BAD_ERROR_ERROR = "Something happened that shouldn't have. Please contact an admin";
const Constants = {
APIResponses: {
accounts: {
login: {
account_not_verified: {
message: "You need to verify your email",
problemFields: ["email"]
},
bad_login: {
message: "Your email or password is incorrect",
problemFields: ["email", "password"]
},
default: {
message: BAD_ERROR_ERROR,
problemFields: ["email", "passwords"]
}
}
}
}
};
export default Constants;
I then import it in a TSX file like this:
import Constants from "constants";
and reference a key like this:
const { status } = json;
const resp = Constants.APIResponses.accounts.login[status.message];
However, when I attempt to use it, I get this error:
Property 'APIResponses' does not exist on type 'typeof import("constants")'. TS2339
69 |
70 | const { status } = json;
> 71 | const resp = Constants.APIResponses.accounts.login[status.message];
This error only occurs when importing it from my baseDir as "constants", when importing as ../../constants, it works fine. (That's the work around I'm going with right now)
Here's the things I've tried that haven't worked:
Explicitly defining a type for Constants in constants.ts
Renaming it to constants.js
Casting the type on the default export
Not exporting it as default
Here's the hacky fixes I found that work, but are hacky and undesirable:
Importing it relatively (../../constants) (current)
Casting it to an any type ((Constants as any).APIResponses...)
Here's my tsconfig.json:
{
"compilerOptions": {
"target": "es5",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react",
"rootDir": "./src",
"baseUrl": "./src"
},
"include": [
"src"
]
}
TodoAppUI.js:15 Uncaught ReferenceError: exports is not defined
I have the export keyword in every class.
For example:
export class mysclass {
public constructor(){}
}
I'm using this syntax to import but still not working
import TodoAppUI = require("./TodoAppUI");
This is my config file:
{
"compilerOptions": {
"module": "commonjs",
"noImplicitAny": true,
"removeComments": true,
"preserveConstEnums": true,
"sourceMap": true,
"target": "es5"
},
"files": [
"TodoApp.ts",
"BaseService.ts",
"TodoAppUI.ts"
]
}
Here is the code that throws the error:
Object.defineProperty(exports, "__esModule", { value: true });
module: 'commonjs' in your config file tells compiler to compile the codes to an commonjs module in which you get a module like const TodoAppUI = require("./TodoAppUI") . If you want to use import, then fix the configFile with module: 'ES6'