Populate an interface property with values - javascript

I have an interface which I want to be populated automatically with some default data when I assign variables to it. The interface is saved in a module and I want to use it on other modules. How do I assign data to interface properties ?
export interface NameAge {
valueList : Array<{ 'Name', 'Age' }>;
}
So I create let's say some variables in multiple modules that implement this interface.
How do I assign some default value to the NameAge property. I can do it with a class containing listUsers as public static property but can't replace interface with class. Also I want the interface and the listUsers array of objects to be in the same module so I when I import the module, I get both the interface and listUsers with the default data already populated. Thanks :))

Interfaces will not allow you to do that.
However you can do it with classes.
class NameAge {
public valueList: Array<{ 'Name', 'Age' }> = [{
'Name': 'J',
'Age': 'ABC'
}];
}
class NameAgeChild extends NameAge {
public child: boolean
public adults() {
return this.valueList.filter(v => v.Age >= 18);
}
}
This however makes sense only if you need a class for this value (code + behaviour together) in your app.
If not, just make a constant with the your values and use it when you create new instances.

Related

How to use enum (attached to a class as a static variable) as a type

// file1.ts
enum Variant {
Success = 'success',
Error = 'error',
}
export class Example {
static Variant = Variant;
}
// file2.ts
import { Example } from './file1';
type Props = {
variant: Example.Variant; // TS2702: 'Example' only refers to a type, but is being used as a namespace here.
};
Typescript throws an error: TS2702: 'Example' only refers to a type, but is being used as a namespace here.
I know I can export the enum itself and use it in file2.ts, but I would like to know why the above example is not working.
Variant is a static class field
export class Example {
// Variant here is a class field
static Variant = Variant;
}
So if you want to use Variant as a type through class Example, you should add typeof before Example.Variant
import { Example } from './file1';
type Props = {
// This statement means that Example is a namespace that contains Variant as a type or class named Variant, which in our case it is a field
variant: Example.Variant; // wrong
variant: typeof Example.Variant; // correct
};
So the error came because you're using class as a namespace

typescript type of dynamic class methods

i am trying to build a class that build some dynamic methods on constructor stage, everything works well, but VS Code auto suggestion does not work for dynamic methods? how should we do that?
here is the codeSandBox
i have also tried interface but still no luck
export default class A {
private version = 1;
get getVersion() {
return this.version;
}
private actions = ["approve", "edit", "delete"];
constructor() {
this.actions.forEach(
method =>
(A.prototype[method] = route => {
console.warn(method + " called");
})
);
}
}
const B = new A();
console.warn(B.getVersion);
console.warn(B.approve()) // auto suggestion not available here
You can do this... but it is really rather hacky. Meta programming like this is impossible to fully type check. Here's a playground with this solution.
First, tell TS that actions is a constant array so that it can narrow the type.
private actions = ["approve", "edit", "delete"] as const;
Next, create a mapped type that describes what your forEach method adds to the type.
type _A = {
[K in A['actions'][number]]: (route: string) => void
}
Note that this has to be a type. It can't be an interface.
Finally, add an interface that extends this type alias. I expected TS to yell at me here about circularity, but apparently this is OK.
export default interface A extends _A {
}

What is the proper way of initializing nullable state in React/TypeScript apps?

What is the proper way to initialize initial empty(null) state in React, using TypeScript interfaces/types?
For example, I have an interface:
interface IObject {
name: string,
age: number,
info: IAnotherObject
}
and a simple component, where I want to define initial information state as null(w/o creating a class that implements my interface and shape default values for all properties), but with IObject interface
interface IState {
information: null|IObject;
}
class App extends React.Component<{}, IState> {
state = {
information: null
};
componentDidMount() {
// fetch some data and update `information` state
}
render() {
<div>
{this.state.information &&
<div>
<div>{this.state.information.name}</div>
<div>//other object data</div>
</div>
</div>
}
Do we have another way to initialize nullable state w/o using union type:
// worked way, but with union null type
interface IState {
information: null|IObject;
}
// some another way(no union), but with initial `null` or `undefined`
interface IState {
information: IObject;
}
state = {
information: null
}
(it's seems not very "smart" for me to annotate with null every object which I want to have initial empty value)type object in state?
If you want to have initial empty state, you should to do this,
Mark information as empty,
interface IState {
information?: IObject;
}
now you can initialize it as empty.
state = {}
You should rely on undefined instead of null for missing properties. From typescript style guide, (link)
Use undefined. Do not use null.

Set literal object to a component's Input property as a known object class

I have a component with an #Input property of type Foo:
#Component({
...
})
export class MyComponent {
#Input() Options: Foo;
...
}
And in the ParentComponent.html literal class pass to it as input value:
<My [Options]="{prop1:true, prop2:false, prop3:1, ... }"></My>
Then type of Options is not Foo anymore. It changes to anonymous object and therefore methods of Foo are not accessible any longer.
One way to prevent this, is to create an instance of Foo in ParentComponent .ts and pass it as variable:
#Component({
...
})
export class ParentComponent {
Options: new Foo(true, true, 1, ...);
...
}
And in the ParentComponent.html use:
<My [Foo]="options"></My>
Another way would be to somehow cast anonymous object to the newly created Foo object:
#Component({
...
})
export class MyComponent {
#Input() set Options(value: Foo){
//Somehow cast anonymous to Foo.
}
private options : Foo;
...
}
Is there any better or built-in way to do that?
If not, then how can I cast anonymous object to Foo?
At some point you need to create an instance of your class Foo with new if you want to use the class methods using one of the two ways you described. If you want to pass in the plain object as your #Input, the input cannot be of (class-)type Foo because it is not an instance of that class. Within the class, you need to call new Foo(fooInput) and then e. g. assign the newly created instance to another member variable.
In my opinion, it would be better to have your data in a plain data object instead of a class: Define Foo as an interface instead of a class for type safety. Then, put the methods from the class into a FooService, which manipulates Foo objects. With this, you don't have to bother with class instantiation.
Example
class User {
constructor(public firstName: string, public lastName: string) {}
getFullName(): string {
return `${this.firstName} ${this.lastName}`;
}
}
becomes:
interface User {
firstName: string;
lastName: string;
}
class UserService {
getFullName(user: User): string {
return `${user.firstName} ${user.lastName}`;
}
}

How to set default parameter for #Input in Angular2?

Say I have a component that will display a name property, so it roughly goes like this:
import {Component, Input} from 'angular2/core';
#Component({
selector: 'demo',
template: `<div>{{name}}</div>`,
styles: [``],
})
export class Demo {
#Input() name: string;
}
The problem is, how could I display [noname] when someone using this component but not passing any name property?
The only solution comes to mind is using logic operator inside template like {{ name || '[noname]' }}.
try
#Input() name: string = 'noname';
with angular 8 and the default tslint settings your IDE will notice:
so it´s okay to just write:
#Input() addclass = '';
without any "type annotation".
In the component you should initialize like:
#Input () name:String='';
In the HTML you can use:
{{ name ===''? 'empty string': name }}
You can intercept #Input() with a setter and have it backed by a private field. In setter, you do a nullcheck so field gets set only to a non-null value. As last, you bind your template to a private fied in which you have set initial value.
I think you can use your idea of using the template. So it would be:
In Component:
#Input () name:String;
In Template:
<div>{{ name != '' ? name : '[no name]' }}</div>
That would check to see if the name is empty, and use '[no name]' or insert the name if the name is passed.
Here is the proper solution to this. (ANGULAR 2 to 9)
Addressing solution: To set a default value for #Input variable. If no value passed to that input variable then It will take the default value.
Example:
I have an object interface named bike:
export interface bike {
isBike?: boolean;
wheels?: number;
engine?: string;
engineType?: string;
}
You made a component named app-bike where you need to pass the properties of bike with #input decorator of angular. But you want that isBike and Wheels properties must have a default value (ie.. isBike: true, wheels: 2)
export class BikeComponent implements OnInit {
private _defaultBike: bike = {
// default isBike is true
isBike: true,
// default wheels will be 2
wheels: 2
};
#Input() newBike: bike = {};
constructor() {}
ngOnInit(): void {
// this will concate both the objects and the object declared later (ie.. ...this.newBike )
// will overwrite the default value. ONLY AND ONLY IF DEFAULT VALUE IS PRESENT
this.newBike = { ...this._defaultBike, ...this.newBike };
// console.log(this.newBike);
}
}
For more detailed article refer to this.
Refer Destructuring assignment from here

Categories