I have the following mainObject which contains key and value pairs and I would like to use this object's value as a key to an interface or state. The following I provided an example of an interface.
//Main Object
export const mainObject = {
NAME:'name',
AGE:'age'
}
Now I want main object values as a key to an interface
interface sample {
'name':string //i want to achieve like this
}
//modifying the below
import mainObject from '';
interface sample {
mainObject.NAME:string //will give error, need to modify this
}
You can do that if mainObject is declared as a runtime constant (not just the mainObject binding, but the object it contains), via as const:
export const mainObject = {
NAME: "name",
AGE: "age",
} as const;
//^^^^^^^^
Then you can get the type of that object via typeof:
type MainObjectType = typeof mainObject;
Then you can use that type to create a mapped type for Sample:
type Sample = {
[key in MainObjectType[keyof MainObjectType]]: string;
};
With your current definition of mainObject, that would make this code valid:
const x: Sample = {
name: "something",
age: "something else",
};
Playground link
If you changed the properties in mainObject, the types would change, and x would need to be updated to have the new properties. For example, if we added EXAMPLE: "example", to mainObject but didn't update x above, we'd get an error that x was missing the example property — playground example.
Related
I am trying to use as a variable to locate a value in an object, basically console.log(myobj.name) but use a variable instead of name e.g.
const myProperty = name:string
console.log(myObj[myProperty])
full details below (including interfaces)
The code runs but I get the following error in VSCODE.
Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'Details'.
below is the code the very last line is the one where I get the typescript error (using strict types)
interface Details {
id:number,
name:string,
email:string
}
interface Items {
[key: string]: Details[],
}
const items: Items = {
"blackberry":[
{
id: 1,
name: 'John Doe',
email: 'john#doe.de'
},{
id: 2,
name: 'Brad',
email: 'lorem#ipsum.com',
}
],
"orange":[{
id: 4,
name: 'Barry',
email: 'john#doe.de'
}
]
}
const myName:string = "name"
const myIx:string = "orange"
// console.log(items[myIx])
console.log(items[myIx][0].name)
console.log(items[myIx][0][myName]) // code runs but TS error here in VScode
You should use the correct type for myName:
const myName: keyof Details = "name"
This also has the advantage, that you get a compile error when you have a typo, e.g. this will fail:
const myName: keyof Details = "Name"
Typescript sees a string passed with object[str] as being able to all kinds of values. You can set the myIx to an enum to force it to be one of the actually existing keys within the interface or the quick and dirty fix can be to cast it to any like this (items[myIx][0] as any)[myName]
I'm looking to create an object from a typescript interface that will store initial empty values:
// Interface I'm looking to mock
interface IMyInterface {
name: string;
posts: string[];
}
From this interface, I would need to initialize an object with initial values based on the interface keys and type, something like:
const initObjFromInterface = (interface: any) => {
// do something
return initObject;
}
const initializedObj = initObjFromInterface(IMyInterface);
Note that I don't know the keys of the interface passed as an argument the initObjFromInterface function!
The returned value of the initObjFromInterface function should be:
console.log(initializedObj);
/* should output:
{
name: "", // empty string
posts: [] // empty array
}
*/
You can't do that as stated in comments.
You have to store somewhere your instance of empty object.
const emptyObject: IMyInterface = {
name: '',
posts: []
}
And pass some unique value to function so it can determine which empty object of interface to get.
const initObjFromInterface = <T>(interfaceName: string) => {
if(interfaceName === "IMyInterface") {
return emptyObject as any as T;
}
return {} as T;
}
I sure the code above can be designed better ( perhaps type guards and type inferring )
How to declare the entire MyCustomObject interface can fallback to an empty object?
interface MyCustomObject {
name: string,
age: number
}
Below case is safe, it has default property of name and age as fallback, but sometime if the obj is from other source like an api, it can be some other type like an empty {} or even an empty []
const obj: MyCustomObject = {
name: "",
age: 0
}
I tried this
interface MyCustomObject {
name: string,
age: number
} | {}
it doesn't work that way.
the most correct way is to create a union
type IUnionType = MyCustomObject | {};
other ways to handle this is to make each property optional
interface MyCustomObject {
name?: string,
age?: number
}
Or create an optional object property where a union is defined in the original interface.
interface MyCustomObject2 {
obj: MyCustomObject | {}
}
examples
I have one interface having some member variables
suppose my interface name is IUser and variable name is iUser.
I have to make it undefine but I m getting error that we cannot make iUser = undefine
so I did something like this.
let obj: any;
this.iUser = obj;
obj = undefined
and it works for me
Imagine having two data types, both of them have an id property:
type Foo = {
id: string,
// other props
}
type Bar = {
id: number,
// other props
}
Now imagine a Table component in React which accepts an array of data as a property. This component needs to be generic, but we know that it requires an id prop for every data object in the data array. This id can either be a number or a string. The props might be typed as follows:
type Props = {
data: Array<{id: number | string}>
}
The problem is that the above notation does not work. Is there a way to be able to 'retype' certain properties?
I have looked into generic types but I can't seem to find a solution without changing the Foo and Bar type definitions.
This is a bit too tricky for Flow to figure out I think. So instead of declaring a single property that both your types share, you can declare your array as containing types Foo | Bar.
This is nice because it has even less repetition than your original code, and in each branch of that if statement you have free access to other unique properties of Foo or Bar without any issues.
// #flow
type Foo = {
id: string,
// other props
}
type Bar = {
id: number,
// other props
}
type Props = {
data: Array<Foo | Bar>
}
const foo: Foo = { id: 'A' }
const bar: Bar = { id: 1 }
const props: Props = { data: [foo, bar] }
for (const item of props.data) {
if (typeof item.id === 'string') {
console.log('I am a Foo')
} else {
console.log('I am a Bar')
}
}
Try Flow Link
I see that ImmutableJS has flow type annotations now but how do I define the type? For example:
const state : ??? = Immutable.fromJS({ name: 'chet', tags: ['something']})
I can define the type from normal JS but how do I say that this is an Immutable.Map with certain keys?
The problem right now is that immutable flow types only support one type definition for each key and value combination.
So immutable.Maps accepts Map<keyType, valueType>
and immutable.List accepts List<valueType>
Immutable.fromJS({ name: 'chet', tags: ['something']})
is equivalent to Map({name: 'chet', tags: List(['something])}
so, your type definition would be
Map<(string) | ('name', 'tags'), string | List<string>>
Use Record instead of Map (or fromJS). It's not the prettiest addition to your code, especially if your state has nested Records, but the type checking support is great.
// #flow
import { Record, List } from 'immutable';
import type { RecordFactory, RecordOf } from 'immutable';
type StateProps = {
name: string,
tags: List<string>,
}
type State = RecordOf<StateProps>;
const makeState: RecordFactory<StateProps> = Record({
name: '',
tags: List(),
});
const state: State = makeState({
name: 'chet',
tags: List(['something']),
});
// Or, to to create an instance of the default
// const _state: State = makeState();
I would write this type as
const state: Map<string, any>
This says that state is going to be of type Map, and the map will have string keys and (name, tags), and the values will be any.
Also, note, you'll have to do
import type { Map } from 'immutable';
or else it will think it's the native type Map and you'll see errors like Map does not have a get or getIn method.