I can't understand this strange JS Flow syntax - javascript

I'm trying to remove some JS Flow typing syntax from an application, but I can't seem to refactor this certain line accurately. It's part of a class declaration:
declare class Error {
static (message?:string): Error;
static call(x: any): void;
static captureStackTrace(x: any, x: any): void;
name: string;
message: string;
stack: string;
}
The problematic line is static (message?:string): Error;.
What is this line doing, and how can I rewrite it without Flow?
It looks like a method but has no name.... Any help would be appreciated!

It's a declaration of a factory function, just merged with class declaration.
So you can type check this: const error = Error(message).
To be less confusing, it can be rewritten as constructor (message?: string): Error.
Then you'll need to use new: const error = new Error(message).
Here is the example of both ways - try flow
So, without flow, you just need to write a function that returns Error object, and maybe check its first argument to be a string if not undefined.

I am not a pro in flow, but i'll try to answer your questions.
What is this line doing
The method accepts an optional string variable named message and it returns an Error object.
how can I rewrite it without Flow?
I think you can use typeof to check the type of the variable. You can find more information here

For my understanding, this definition is to check the input type of primitive Error Object.
This kind of Flow semantic is called library definition which is used to define type check for 3rd party library. You could see the definition below.
Flow defintion
So what you need to do is create a customized Error function say myError and check the first argument type as string. Then return Error Object.

Related

Typescript not enforcing that non-optional parameters / identifiers are provided to callback function argument [duplicate]

interface IConverter {
convert(value: number): string
}
class Converter implements IConverter {
convert(): string { // no error?
return '';
}
}
const v1: IConverter = new Converter();
const v2: Converter = new Converter();
v1.convert(); // error, convert has parameter, although Converter's convert doesn't expect one
v2.convert(); // ok, convert has no parameters, although Converter implements IConverter which should has paramater
Converter implements IConverter, which has a method with one parameter, but Converter lacks this parameter. Why TS compiler doesn't raise an error if we do not fully implements this interface?
Typescript uses structural typing to determine type compatibility. For functions this means that you don't need to have the exact same signature for the declaration and the implementation, as long as the compiler can determine the implementation to be safe to call through the declaration.
In this case this boils down to, a function with less parameters can be an implementation for a function declaration with more parameters as the extra passed in parameters will be ignored by the implementation, so no runtime error can occur because of this (for the most cases anyway, there might be corner cases on things that depend on Function.length)
The reason you get an error on v1 but not v2 is because once the assignment is done the compiler only knows the type of the variable not what you originally assigned into it and will check based on the actual type of the variable. So for v1 this means IConverter.convert requires a parameter, no way to know it does not. For v2 it will check Converter.convert which is known to require no arguments.
TypeScript allows a function that takes fewer parameters to be treated as a function type that takes more parameters, because the extra parameters will just be ignored at runtime. See the handbook.

Declaring object property type in function param

This problem has me so confused, I don't even know how to properly title this question.
I started learning TypeScript a couple of hours ago and I've run into a brick wall with a Property 'whatever' does not exist on type 'object'. intellisense error.
I get the feeling that I'm supposed to declare an interface for this object that defines the property in question, but that feels wrong, since the object is an event listener parameter, so I don't call the function directly - the library that I'm using does. It also feels like an interface for something that I'll literally only use once is overkill.
Let me show you what I mean:
// this event listener is created by the library I'm using, so I can't change the params it sends
this.load.on('fileprogress', (file: object) => {
console.log(`Loading asset: ${file.key}`); // ".key" is crapping out with "Property 'key' does not exist on type 'object'."
});
The easiest solution seems to be just doing (file) => {...} so that it defaults to type any, but I want to avoid that, otherwise I might as well go back to using plain old JavaScript. The next thing I came up with is declaring interface File { key: string } above the event listener and then changing the function to (file: File) => {...}, but that feels like a hack because, like I said, I'm not the one calling this function and have zero control over what's actually getting sent as the parameter. Also, the whole interface File... feels overly verbose.
What I was thinking, along the interface lines, is isn't there some way to inline declare the properties' types in the parameter list. Something like (file: object { key: string })? This will still be a hack if we think of it literally as an inline interface, but at least we're now telling it what properties the object is expected to have, right?
One thing I came up with which isn't throwing an intellisense error is this monstrosity (file: {[key: string]: any} = {}) => {}, but there's that dreaded any keyword again, and intellisense is reporting this as a... Well, I don't even know what it's supposed to be. Here's a screenshot.
I guess the bottom line question that I'm asking is what's the correct way to handle this type of situation where a function that you don't call directly needs to receive an object with properties you can't predict and thus can't define?
The idiomatic TypeScript typing for this is Record<string, unknown>. That assumes that you will receive an object with string property names, but assumes nothing about specific property names or what types their values might have. You will then need to perform runtime narrowing tests in order to safely access any property of this object. So, for example:
this.load.on('fileprogress', (file: Record<string, unknown>) => {
if (typeof file.key === 'string') {
console.log(`Loading asset: ${file.key}`);
}
});
(Note that the typeof guard here is not necessary to simply say .key, as a Record object will allow any string property name. It is necessary to confirm the value's existence and type [I guessed string]).
However, are you sure that the library you are using makes absolutely no representation about the shape of the object it passes to you? That seems unusual even for a plain JS library. Many, if not most, libraries even have their own TypeScript typings now (either directly or through #types/* packages), making these argument types implicit.

Property 'replace' does not exist on type 'IData'. What is missing?

I am tryign to create a cast function where a string with numbers is received as input, but when using the regex function "replace()" it doesn't recognize it.
Anyone know how can I declare "replace()" inside type IData?
interface IData{
data:string | {
replace: (()=>number),
};
}
class Cast{
castString(data:IData){
return data.toString();
}
castNumber(data:IData){
return data.replace(/\D+/g, '');
}
}
export default new Cast;
Based on the comments so far, I think that there is a need to shortly explain types in JS (which translates to TS as well).
Primitive types in JavaScript (e.g. string, number and boolean) are no objects with functions, but rather only values. The functions you can call on them (like replace for a string) are actually from the so called Boxed types. They are the Object counterpart to the primitives. They can be used to either explicitly cast (Number("123")) or to wrap the primitive in an object (new Number(123)).
Latter should be avoided, because this id done automatically by JS whenever needed. Like in your case: "34bbb5".replace(...) is changed to new String("34bbb5").replace(...) behind the scenes
How does this information help?
With that in mind, your goal seems to be to define a string type that has a different replace function definition. The new definition was meant to change the behavior of the Sting.replace function. Problem being, that it is only a definition. So, it does not affect the actual String.replace function.
What are your options
A TypeScript interface is only a definition present at compile time. So, by overloading the replace function definition you do not gain any code change. Consequentially, the following interface definition should suite your case just fine.
interface IData {
data:string
}
Your Cast class seems to have the task to cast the IData either to a string or to a number. The first one is rather simple, as it means to simply return the value of the data attribute.
You used the toString() method of the object that fullfills the IData interface. This actually calls the Object.toString() function which results in "[object Object]".
The second cast method should return a number. Here you already provided most of the code, but you missed to call it on the data attribute (as pointed out by #jcalz).
So, you should call it on the data attribute and actually cast it to a number. You can use + or Number() or parseInt() for this. As you only have numbers in the string left, there is no difference in behavior.
The result would look like:
interface IData{
data:string;
}
class Cast{
castString(data:IData){
return data.data.toString();
}
castNumber(data:IData){
return Number(data.data.replace(/\D+/g, ''));
}
}
export default new Cast;
Tip
You could also use object destructuring if you want
castString({data}: IData){
return data.toString();
}

check if an object have all the property of a class in typescript

I have a class abc in typescript
export class ABC{
public a : any;
public b : any;
public c? : any;
public d? : any;
}
Now in my function I receive an input lets say data:any
Now i want to check if that input i.e data have all the required property of class ABC.
Is there any option to do this without using hasOwnProperty on every key of class ABC and object data.
Instantiate the class to get all properties and use the every function to check if all keys are indeed in the passing object.
function hasAllProperties (data, YourClass) {
const yourClass = new YourClass()
return Object.keys(yourClass).every((key) => data[key] !== undefined)
}
Usage
hasAllProperties(data, ABC)
TL;DR. Using hasOwnProperty is very likely inevitable, unless you’re absolutely sure about your function usage, input data source to your function is totally under control.
Longer version:
You must understand the difference of static type check and runtime check. TS is a static type checker, but it doesn’t have a runtime. Code in TS is transpiled to JS before execution, and run in a JS runtime engine.
The only way TS can check whether properties of data meet the requirement, is that piece of information must first exist within the type system. But in your case data: any has swallowed all meaningful type information, leaving TS nothing to work on.
Even if data’s type is more concrete, something like data: { a: any; b: any; } etc. TS can only check that, in your code base, you didn’t explicitly write any code that pass in invalid arguments.
If somewhere you write validate(anyData), where anyData: any, then the safe guard is gone. Plus, it cannot guarantee in JS runtime, invalid arguments are never passed in.

When testing TypeScript code, can we trust the declared types of parameters or should we test as if we're testing JavaScript code?

I'm trying to test a method I've written in TypeScript. Let's say the method signature is as below:
doTask(value: number): void
Each method parameter is labeled with a type, but I'm worried that the caller will accidentally pass an argument of the wrong type into the method, as such:
let wrongValue: any = '3';
doTask(wrongValue);
Therefore, in my tests I want to try passing in arguments of the wrong type and checking to make sure my method doesn't fail silently. However, this requires a lot of extra testing for each parameter in the method and I'd have to do this for each method. It would be like testing JavaScript code.
I know I could strengthen my specification for this method and tell the caller that this method can ONLY be called with a number, but I don't want to put that much responsibility on the caller. I know I can also use type guards within my method, but I don't want to test based on the implementation.
I'm new to TypeScript and I'm wondering if there's some accepted practice in this situation? Thanks so much!
It depends on where the parameter/value comes from. Typescript is there to prevent you from INTENTIONALY passing the wrong type. If the value comes from say a text input then you know its a string, hence you convert it to a number before you call the function.
Another concept is the concept of type guards that you implement. Read official docs here
Edit: Clarification: Typescript types are not available in javascript. The type specifications in typescript exist for your convinience. The parameter and return type declaration in your method is gone when compiled to javascript: doTask(value: number /*<- this is removed in JS */): void /*<- this is removed in JS */
e.g
function doTask(value: number) {
//type of value passed is unknown at runtime?
if(typeof value !== 'number') {
// throw some error
}
//else continue
}

Categories

Resources