RequireJS module declarations in TypeScript - requirejs

I've recently updated to TypeScript 0.9.5 which is now throwing up new errors during compilation.
I'm defining an AMD module using RequireJS as outlined HERE.
define('myModule', [
'angular',
'config'
], function (angular, config) {
'use strict';
...
});
The TypeScript definition file for RequireJS has the following definition for RequireDefine:
/**
* Define a module with a name and dependencies.
* #param name The name of the module.
* #param deps List of dependencies module IDs.
* #param ready Callback function when the dependencies are loaded.
* callback deps module dependencies
* callback return module definition
**/
(name: string, deps: string[], ready: (...deps: any[]) => any): void;
However I'm getting the following errors:
error TS2082: Build: Supplied parameters do not match any signature of call target:
error TS2087: Build: Could not select overload for 'call' expression.
The intellisense error states:
Call signatures of types '(angular:any, config:any) => any' and '(...deps: any[]) => any' are incompatible.
Is the definition file incorrect? Where am I going wrong with the callback parameters?
Further Information:
Changing the declaration to the following now compiles.
define('myModule', [
'angular',
'config'
], function (...args:any[]) {
'use strict';
...
});
However moving to a single parameter object is surely a backwards step? Is this a limitation of the definition file or the TypeScript compiler?

Is this a limitation of the definition file or the TypeScript compiler?
It's both. It's a "limitation" of the TypeScript compiler (limitation is quoted since it is enforcing a valid constraint here), and can be fixed from the definition file.
It's actually a lot simpler to reproduce this case:
function argLoving(fn: (...deps: any[]) => any){
}
argLoving(function(x,y){ // <- compile error
});
The issue is - while you can call the function in argLoving with x and y when you declare or supply it - it has to actually accept varargs to not break type safety.
Imagine the following:
function argLoving(fn: (...deps: any[]) => any){
}
function foo(x:any,y:any){
}
argLoving(foo);
Now it's clear that argLoving is accepting a function that works on a variable number of arguments but foo only works on exactly two.
That's the type problem.
The way C# solves this is pretty ugly* (with Func for example), so if you're looking for a quick& dirty fix - what you can do is just define multiple signatures in your .d.ts file:
This of course compiles with no problem:
function argLoving(fn: (x:any) => any)
function argLoving(fn: (x:any,y:any) => any)
function argLoving(fn: (x:any,y:any,z:any) => any)
function argLoving(fn: (x:any,y:any,z:any,a:any) => any)
function argLoving(fn: (...deps: any[]) => any){
}
function foo(x:any,y:any){
}
argLoving(foo); // this compiles now
* http://msdn.microsoft.com/en-us/library/bb534960(v=vs.110).aspx - look at all the Action and Func overloads in the left
Update:
After I opened an issue on GitHub - the author of DefinitelyTyped put pull request and for this issue using the same fix suggested here https://github.com/borisyankov/DefinitelyTyped/issues/1434 . It's being discussed here https://github.com/borisyankov/DefinitelyTyped/pull/1435

For function definitions that take anything and return anything the new recommended syntax is :
function require(fn: Function){
}

Related

React-testing-library and <Link> Element type is invalid: expected a string or a class/function but got: undefined

I'm using react-testing-library to test a simple component which has a nested react-router-dom's <Link> inside of it and I am getting this error:
Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.
I solved it by mocking the Link:
jest.mock('react-router-dom', () => ({
Link: jest.fn().mockImplementation(({ children }) => {
return children;
}),
}));
That way I can test my component normally:
test('render MyComponent with <Link>', async () => {
const myListOfLinks = mockLinks();
render(<MyComponent parents={myListOfLinks} />);
const links = await screen.findByTestId('my-links');
expect(MyComponent).toBeInTheDocument();
});
I had similar issue with Link component being undefined. In our case it was caused by existing jest mock. There was a file in our codebase under __mocks__/react-router-dom.js which didn't provide an implementation for Link. So all other tests were using mocked implmentation of react-router-dom module. Jest uses convention for automatic mocking of modules. Removing this mock solved the issue

error TS2749: 'IRequestWithUser' refers to a value, but is being used as a type here. Did you mean 'typeof IRequestWithUser'?

I am extending Request in express library to contain a user property:
import { Request } from 'express';
export default interface RequestWithUser extends Request {
user: {
user_name: string;
password: string;
}
}
The title error appears in the first parameter annotation:
import IRequestWithUser from '../shared/interfaces/isRequestWithUser';
const router: Router = Router();
router.post('/myRoute', async (req: IRequestWithUser, res: Response) => {
/*
error TS2749: 'IRequestWithUser' refers to a value,
but is being used as a type here. Did you mean
'typeof IRequestWithUser'?
*/
I don't believe interfaces are values. They should purely be types. So what is causing this error?
Also tried
typeof IRequestWithUser This results in No overload matches this call
Perhaps there's something missing from your code snippets, but the only error I get on Typescript 3.9 is one that relates to the overload error that you allude to later in your question.
Because the .post() call is looking for a callback that expects type Request, you can't type your req argument as IRequestWithUser. This is because, although IRequestWithUser will contain all properties of Request, the Typescript compiler can't guarantee that the extra properties you plan to have in req will be there when the callback is run.
The recommended way to extend Express types is to use interface merging. This allows you to "redefine" the Request type altogether, but it's a global definition so it gets messy:
// definitions/express/index.d.ts
import * as express from 'express' // Unfortunately, you need some kind of import here to make this a valid module.
declare module "express-serve-static-core" {
export interface Request {
user: {
user_name: string;
password: string;
}
}
}
Your compiler should pick this file up on its own and support your new property using the original Request type. You might need to have an explicit typeRoots set in your tsconfig.json if you're using something like ts-node though.

Typescript - Add types for 'promisifed' methods

I have a very simple module that wraps the "fs" module.
This module simply "promisifies" all of "fs" methods and exports the new object:
fsWrapper.ts
import Promise from "bluebird";
import * as fs from "fs";
const fsWrapper = Promise.promisifyAll(fs);
export = fsWrapper;
Now I can use this wrapper instead of "promisifiying" the "fs" module inside every caller module, like so:
main.ts
import fsWrapper from "./fsWrapper";
function test(): void {
fsWrapper.readFileAsync("tst.txt", "utf8")
.then((data: Buffer) => {
console.log("data:", data.toString());
})
}
This of course doesn't work with typescript since "fs" doesn't hold the readFileAsync method and I receive a compiler error.
While searching of ways of properly typings this wrapper, I found the following typescript issue.
Using its approach, I can create my own fsWrapper.d.ts, in which I need to manually add the *Async methods, similar to the following:
fsWrapper.d.ts
import Promise from "bluebird";
declare module "fs" {
export function readFileAsync(path: string, options: { encoding?: string | null; flag?: string; } | string | undefined | null) : Promise<Buffer>;
...
}
The problem here is:
Manually adding all the needed methods is tedious and error prone.
If these methods will change in future versions, I will have no idea as my code will keep compiling and I will suffer runtime exceptions.
I know that util.promisify is properly typed, and therefore thought of somehow extending "fs" with these new methods, similar to the following:
fsWrapperTest.ts
import * as fs from "fs";
import { promisify } from "util";
let fsWrapper = fs;
fsWrapper.readFileAsync = promisify(fs.readFile);
export = fsWrapper;
But this outputs an error that indicates that I can't extend existing modules:
error TS2551: Property 'readFileAsync' does not exist on type 'typeof "fs"'. Did you mean 'readFileSync'?
Is there any way for me to properly type this wrapper while keeping "up-to-date" typings and working with Intellisense?
Edit:
Just to clarify, I understand how to create an object that will have all "fs" methods promisified (that was the case with the original fsWrapper.ts above).
What i'm struggling is typing it properly for Intellisense usage.
For example, running the following:
import * as fs from "fs";
import { promisify } from "util";
let fsWrapper = Object.keys(fs).reduce((p: typeof fs, v: string) => { p[v] = promisify(fs[v]); return p }, {})
Will give me a fsWrapper: {} typing for the object.
I would like to have all of "fs" methods + the new 'promisifed' methods as its type.
EDIT - selected solution
I ended up using the declare module 'fs' approach by extending the 'fs' module with all the *Async methods that I was using in my code.
Not ideal but it seems that there is no better alternative..
If you are using TypeScript 3.0 or newer, the improvements to mapped types and parameter tuples should make this possible. For example:
type UnpackedPromise<T> = T extends Promise<infer U> ? U : T
type GenericFunction<TS extends any[], R> = (...args: TS) => R
type Promisify<T> = {
[K in keyof T]: T[K] extends GenericFunction<infer TS, infer R>
? (...args: TS) => Promise<UnpackedPromise<R>>
: never
}
That creates a type for unwrapping a promise, a type for a generic function, and then an inferred mapped type for the promisified version that maps each key to a new value that is the promisified version of the previous function. To use that type (playground link):
const original = {
one: () => 1,
two: () => 2,
add: (a: number, b: number) => a + b
}
const promisified = original as unknown as Promisify<typeof original>
promisified.one()
promisified.add(1, 2)
And for your example, here's how that would be used:
import * as fs from "fs";
import { promisify } from "util";
let fsWrapper = Object
.keys(fs)
.reduce((p: typeof fs, v: string) => { p[v] = promisify(fs[v]); return p }, {}) as unknown as Promisify<typeof fs>
Notice that you are casting to unknown and then to Promisify<*> - this is because by default it will consider the mapped type to be possibly a mistake.
Node.js >= v10.x started to provide already promisified functions.
You can use them via require("fs").promises or require("fs/promises") (>= v14.x)
https://nodejs.org/docs/latest-v10.x/api/fs.html#fs_fs_promises_api
https://nodejs.org/docs/latest-v14.x/api/fs.html#fs_fs_promises_api
I use
const fs = { ...require("fs"), ...require("fs/promises") };
There is an existing thread on github exactly for this, see https://github.com/Microsoft/TypeScript/issues/8685
So, you can do something like:
import { Promise } from 'bluebird';
import * as fs from "fs";
declare module "fs" {
interface fs {
[method: string]: any;
}
}
const fsWrapper = Promise.promisifyAll(fs);
export = fsWrapper;
This is already very similar to what you were doing to get past the issue of calling the methods.
Please note that as far as I know the only way to allow full Intellisense is to do module augmentation as described in the typescript documentation https://www.typescriptlang.org/docs/handbook/declaration-merging.html
But that implies you manually writing the interface for the new methods generated by the promisified inside that module structure above. i.e.
declare module "fs" {
interface fs {
readFileAsync(... ),
writeFileAsyng(... ),
etc ...
}
}

Node.js lazy require module

I'm writing a lazy module require() or import
const lazy = new Proxy({},
{
get: function (target, name) {
console.log('lazy require', { target, name })
return require(name)
}
}
)
/**
* #param {string} Module Name
* #example const expo = requirez('expo')
*/
export default function requirez(name) {
return lazy[name]
}
and oddly when I run it I get:
Cannot find module "."
The console.log statement logs:
lazy require {target: {…}, name: "./Linking"}
So require(name) should be getting called as: require("./Linking")
Not as require(".") which the error is indicating.
Found a related bug report:
https://github.com/webpack/webpack/issues/4921
Since node require tree resolution is evaluated/analyzed statically and webpack assumes this it fails on dynamic resolution.
Also, on browser webpack will have to transpile the required bundle before running in the browser thus dynamic lazy requires can't be run after transpilation. You'd be missing the transpiled source of that required module.
I've tried using import() but it also has bugs:
https://github.com/webpack/webpack/issues/4292#issuecomment-280165950

anonymous functions in typescript are not converted to javascript properly

I'm writing my application using nodejs 5.10.1 with tsc 1.8.9
In general i'm converting the syntax of anonymous functions to typescript syntax. which means converting
function(){}
to
()=>{}
so I have several places where I changed to the typescript method of declaring anonymous functions:
async.each(params.fjso, (filePath:string, callback:Function)=> {
is converted to:
async.each(params.fjso, (filePath, callback) => {
another example:
async.forEachOf(params.fjso, (filePath:string, fileIdx:number,callback:Function)=>{
is converted to:
async.forEachOf(params.fjso, (filePath, fileIdx, callback) => {
as you can see the code part ()=>{} is not modified to function(){}.
I'm new to typescript so i'm probably missing something... but what?
tsc complication doesn't show any error messages.
thanks

Resources