Is it possible to manually define additional methods for an existing class?
My specific usecase is bluebird's promisifyAll() which:
Promisifies the entire object by going through the object's properties and creating an async equivalent of each function on the object and its prototype chain… http://bluebirdjs.com/docs/api/promise.promisifyall.html
Obviously, flow wouldn't be able to figure this out automatically. So, I'm willing to help it. The question is HOW?
Consider the following code
import http from 'http'
import { promisifyAll } from 'bluebird'
promisifyAll(http)
const server = http.createServer(() => { console.log('request is in'); })
server.listenAsync(8080).then(() => {
console.log('Server is ready to handle connections')
})
Flow gives the following error here:
property `listenAsync`. Property not found in
Server
There wouldn't be any error if I used listen. flow's smart enough to see that this is a real method defined in a module. But listenAsync is a dynamic addition by promisifyAll and is invisible to flow
That's not possible and that would not be really safe to do. Here is something you can do for your case:
first declare bluebird as following:
declare module "bluebird" {
declare function promisifyAll(arg: any): any
}
Then do this:
import httpNode from 'http'
import { promisifyAll } from 'bluebird'
import type { Server } from 'http'
type PromisifiedServer = Server & {
listenAsync(port: number, hostname?: string, backlog?: number): Promise<PromisifiedServer>;
};
type PromisifiedHttp = {
createServer(listener: Function): PromisifiedServer;
};
const http: PromisifiedHttp = promisifyAll(httpNode)
Here we manually cast http to type PromisifiedHttp. We still have to declare all promisifed types manually, although we can use type intersection to extends existing types.
Related
I am trying to use commands.ts to implify repeating actions, like asking for email and password.
but when I try to use this, it gives me the rror for the login(Argument of type '"login"' is not assignable to parameter of type 'keyof Chainable)
`
Cypress.Commands.add("login", (email, password) => {
some codes....
})
`
I didn't try anything else
create a file index.ts on the same level as commands.ts
put inside
export {}
declare global {
namespace Cypress {
interface Chainable {
login(email:string, password:string): Chainable<void>;
}
}
}
enjoy
UPDATE - WHY IT WORKS
When we use Typescript everything needs an interface to be described.
In your example, Cypress has its Chainable interface but you wish it to have a 'login' property too.
With this code, you are saying that you want to extend 'Cypress.Chainable' to have a login property.
Let me explain it to you with another real example
interface String {
myOwnChainableFunction: Function
}
String.prototype.myOwnChainableFunction = function (this: string){
console.log(this)
}
'ciao'.myOwnChainableFunction();
In this example, we are extending the String prototype to accept our own function in chain.
In Javascript, we have just to attach it to the prototype but with .ts the Typescript checker pretends this property exists in the String interface, so we added it to the interface too.
I'd like to use the window.showDirectoryPicker() method from Dart/Flutter. Using it from JavaScript to allow a user to select a directory and then list the contents of it works like this:
const dirHandle = await window.showDirectoryPicker();
for await (const entry of dirHandle.values()) {
console.log(entry.name);
}
The docs for FileSystemDirectoryHandle.values() say it returns "a new Array Iterator containing the values for each index in the FileSystemDirectoryHandle object." It doesn't say specifically, though it appears to be an async iterator as enumerating it requires await for.
I'm trying to create bindings to call this from Dart. I expect that my binding should return a Stream<FileSystemHandle> although I can't find any examples or docs about doing this. I've tried many combinations of things, though here's what I have now:
#JS()
library js_lib;
import 'dart:html';
import 'package:js/js.dart';
import 'package:js/js_util.dart';
Future<FileSystemDirectoryHandle> showDirectoryPicker() =>
promiseToFuture(callMethod(window, 'showDirectoryPicker', []));
#JS()
abstract class FileSystemDirectoryHandle extends FileSystemHandle {
external Stream<FileSystemHandle> values();
}
#JS()
abstract class FileSystemHandle {
String get name;
}
#JS()
abstract class FileSystemFileHandle extends FileSystemHandle {}
I'm trying to call it like this:
final dirHandle = await showDirectoryPicker();
await for (final item in dirHandle.values()) {
print(item.name);
}
However this fails like with the error:
Error: Expected a value of type 'Stream', but got one of type 'NativeJavaScriptObject'
I suspect I may need something to convert the JS iterator to a Dart stream (similar to the promiseToFuture call I have), although I can't find a way to do that (I'm also unable to add methods to FileSystemDirectoryHandle that aren't external.
Edit: This seems to work in build_runner serve but not build_runner serve --release. I've filed an issue to determine if this is a bug or this is the wrong way to do this.
After realising the async iterators in JS are just iterators where next() returns a promise, I was able to write a small function that just invoked the method manually and yielded in an async* function:
extension FileSystemDirectoryHandleExtensions on FileSystemDirectoryHandle {
Stream<FileSystemHandle> values() =>
_asyncIterator<FileSystemHandle>(callMethod(this, 'values', []));
}
Stream<T> _asyncIterator<T>(jsIterator) async* {
while (true) {
final next = await promiseToFuture(callMethod(jsIterator, 'next', []));
if (getProperty(next, 'done')) {
break;
}
yield getProperty(next, 'value');
}
}
This is called using the original Dart code above, and prints the filenames as desired.
I am new to typescript and noticed a behaviour I wasn't expecting. When I go to mock a named import with the js-cookie package, it mocks a particular instance of the property, but incorrectly chooses the correct type to use.
import Cookies from "js-cookie"
import { mocked } from "ts-jest/utils"
jest.mock("js-cookie")
const mockedCookies = mocked(Cookies, true)
beforeEach(() => {
mockedCookies.get = jest.fn().mockImplementation(() => "123")
})
it("SampleTest", () => {
//this line throws an error as it is using a difference interface to check against
mockedCookies.get.mockImplementationOnce(() => undefined)
//continue test...
}
In the #types/js-cookies there are two definitions for this and it always uses the get() version when I reference it as mockCookie.get.<some-jest-function>. Hence, I get typescript errors saying Type 'undefined' is not assignable to type '{ [key: string]: string; }'..
/**
* Read cookie
*/
get(name: string): string | undefined;
/**
* Read all available cookies
*/
get(): {[key: string]: string};
I can fix this by always redeclaring the jest.fn() every time, but would prefer to use the handy jest functions (like mockImplementationOnce).
Am I doing something wrong? Is there a way to enforce which get type to use?
I am not sure if this helps but you may use mockedCookies.get.mockImplementationOnce(() => ({})) as of now to get rid of this error.
Updated:
After some testing, I see that these mock functions are stored in dictionary style format. The last function with same name always replace the previous ones.
If you try to move get(name: string) below get() from 'js-cookie', you will see that this error is gone.
So there is no way to get specific function with same name.
I don't know much about your use-case, but you may do better not using jest mocks and instead use jest's jsdom environment. You can then set up your environment using js-cookie's methods.
In your example, you can run Cookies.remove(<cookie name>); as setup, then when you run Cookies.get(<cookie name>), you should get undefined, no? You do have to be careful about state persisting between tests, but you have to do that in the mock version of this anyway.
I tested jsdom's implementation for a couple simple cases, but I have no idea how thorough their mock is for more advanced use cases involving multiple domains, etc.
I'm trying to create some TypeScript definitions for modules that already exist. In a particular interface to be implemented, the signature looks like this:
type NextFunction<T> = () => T;
type Response = string[] | Promise<string[]>;
interface IPage {
getBodyClasses(next: NextFunction<Response>): Response;
}
The parameter and return structures are fixed, and I'd really like to be able to have TypeScript infer what the parameter types are of my overridden methods. However, when I create my override, I see that the parameter implicitly has an any type.
class Page implements IPage {
getBodyClasses(next) {
return next();
}
}
Is there any way to mark getBodyClasses as a dedicated override so that the types for parameters are automatically inferred? It would already say that Page was improperly implementing the interface if I typed next as number, so I don't quite understand why it can't also then infer the type of next is the same as the interface's.
Contextual typing of implemented properties is not supported.
More
The main issue that tracked this is https://github.com/Microsoft/TypeScript/issues/1373
There are 2 things that I'm very confused about.
What is the advantage of using any of ES6 class or Object literals.
And where should I use any of them?
Some of the examples that I'm trying out are mentioned below. Please let me know when to use particular way of implementation and when not to.
Class Example 1:
// auth.js
class Auth {
login(req, res) {...}
signup(req, res) {...}
}
module.exports = new Auth();
// index.js
const auth = require('auth');
Class Example 2:
// auth.js
class Auth {
login(req, res) {...}
signup(req, res) {...}
}
module.exports = Auth;
// index.js
const Auth = require('auth');
const auth = new Auth();
Object Literal Example:
// auth.js
module.exports = {
login: (req, res) => {...},
signup: (req, res) => {...}
};
// index.js
const auth = require('auth');
What I think from reading about them is that:
Class Example 1:
You can not create more than 1 object. Because a module is only executed once. So, on every import you will get the same object. Something similar to singleton. (Correct me here if I misunderstood it)
You will not be able to access the static methods of the class because you are only exporting the object of the class.
Class Example 2:
If you have a class that contains nothing but helper methods and the object does not have any state, It makes no sense creating object of this class all the time. So, in case of helper classes, this should not be used.
Object Literal Example:
You can not do inheritance.
Same object will be passed around on every require. (Correct me if I'm wrong here as well)
Please help me understand these concepts, what I'm missing out, what I've misunderstood and what should be used when and where. I'll be very grateful for your help.
Feel free to edit the question, if you think I made a mistake somewhere.
Class Example 1: You can not create more than 1 object. Because a module is only executed once. So, on every import you will get the same object. Something similar to singleton.
Correct. This is an antipattern therefore. Do not use it. class syntax is no replacement for object literals.
You will not be able to access the static methods of the class because you are only exporting the object of the class.
Theoretically you can do auth.constructor.… but that's no good.
Class Example 2: If you have a class that contains nothing but helper methods and the object does not have any state, It makes no sense creating object of this class all the time. So, in case of helper classes, this should not be used.
Correct. Use a simple object literal instead, or even better: multiple named exports instead of "utility objects".
Object Literal Example: You can not do inheritance.
You still can use Object.create to do inheritance, or parasitic inheritance, or really anything.
Same object will be passed around on every require.
Correct, but that's not a disadvantage. If your object contains state, you should've used a class instead.
If your class has got a constructor, you can build several objects from this class threw :
var Panier= require('./panier');
var panier1 = new Panier(13, 12, 25);
var panier2 = new Panier(1, 32, 569);
Of course your Panier would be defined in the file Panier.js located in the same directory :
module.exports = class Panier
{
constructor(code, qte, prix)
{
this.codeArticle = code;
this.qteArticle = qte;
this.prixArticle = prix;
}
getCode()
{
return this.codeArticle;
}
getQte()
{
return this.qteArticle;
}
getPrix()
{
return this.prixArticle;
}
}