Flow object with unknown number of fields - javascript

How do I define the type of an object that I know has two fields for sure: id and name, and an unknown other number of fields which names follow the pattern string_string.
So, for example, an object I would have is:
{id: '1', name: 'john', 'fa2_oh': 'value'}
I know how to define the type if the object just had id and name:
{ id: string, name: string }
But how do I define the other potential keys?

As Aleksey said, you can add an indexer property, but it's not possible to specify a pattern of strings.
type BaseT = { a: string, b: number, [string]: boolean }
const foo: BaseT = { a: 'a', b: 1, hello: false }
If the pattern is somewhat bounded, you could spell out that type:
type KeysT = 'fa_oh1' | 'fa_oh2' | 'bb_aa1' | 'bb_aa2'
type BaseT = { a: string, b: number, [KeysT]: boolean }
const foo: BaseT = { a: 'a', b: 1, hello: false }

Related

Convert Array to Map Typescript

I'm trying to covert the following array to a Map:
const arr = [
{ key: 'user1', value: { num: 0, letter: 'a' } },
{ key: 'user2', value: { num: 0, letter: 'b' } },
{ key: 'user3', value: { num: 0, letter: 'c' } },
];
What I have so far:
const arr = [
{ key: 'user1', value: { num: 0, letter: 'a' } },
{ key: 'user2', value: { num: 0, letter: 'b' } },
{ key: 'user3', value: { num: 0, letter: 'c' } },
];
const b = arr.map(obj => [obj.key, obj.value]);
const map = new Map<string, { num: number; letter: string }>(b);
console.log(map.get('user1'));
Do you know if this is something achievable?
PS: You can find Typescript playground here and the error that I'm getting
obj.key is a string, and obj.value is a {num: number, letter: string}, so if you make an array of them ([obj.key, obj.value]), then you get an Array<string | {num: number, letter: string}>.
You need to tell TypeScript that you're creating a specific tuple, as expected by the Map constructor, not a generic array. There are a few ways of doing this:
// The simplest: Tell TypeScript "I mean exactly what I said."
// (This makes it readonly, which isn't always desirable.)
const b = arr.map(obj => [obj.key, obj.value] as const);
// Or be explicit in a return type. You can do this at a few levels.
const b = arr.map<[string, { num: number, letter: string}]>(obj => [obj.key, obj.value]);
const b = arr.map((obj): [string, { num: number, letter: string}] => [obj.key, obj.value]);
// Or, as explained in the comments, do it in one line, and TypeScript
// can infer tuple vs. array itself.
const map = new Map(arr.map(obj => [obj.key, obj.value]));

Crating a type of array of multiple objects

I want to create a type for an array of objects. The array of objects can look like this:
const troll = [
{
a: 'something',
b: 'something else'
},
{
a: 'something',
b: 'something else'
}
];
the type i am trying to use is:
export type trollType = [{ [key: string]: string }];
Then i want to use the type like this:
const troll: trollType = [
{
a: 'something',
b: 'something else'
},
{
a: 'something',
b: 'something else'
}
];
but i get this error:
Type '[{ a: string; b: string; }, { a: string; b: string; }]' is not assignable to type 'trollType'.
Source has 2 element(s) but target allows only 1
I can do something like this:
export type trollType = [{ [key: string]: string }, { [key: string]: string }];
but lets say my array of object will have 100 objects in the array.
When setting a type for an array it should in this format any[].
So in your case
export type trollType = { [key: string]: string }[];
You can try to use the Record type for storing the object attribute definitions and create an array out of it, like in the following:
type TrollType = Record<string, string>[];
const troll: TrollType = [
{
a: 'something',
b: 'something else'
},
{
a: 'something',
b: 'something else'
}
];

Angular 11 can't access object property dynamically

I have to access to a subset of my object properties and store the label and the value in another array. Basically I have this object:
myObject: CustomObject {
prop1: value1,
prop2: value2
prop3: value3
}
and as output I need an array with objects of type: {label: string, value: number}.
I need to fill this array with prop1 and prop2 and their values, so that I have something like this:
myArray = [{label: prop1, value: value1}, {label: prop2, value: value2}]
What I've tried is this:
labels = ['prop1', 'prop2'];
labels.forEach((l: any) => {
this.myArray.push({ label: l, value: this.myObject[l] })
})
or also this.myObject.l
But I get this error:
Element implicitly has an 'any' type because expression of type 'any'
can't be used to index type 'MyObject'
I've changed the type of "l" to string but I got:
No index signature with a parameter of type 'string' was found on type
What's the correct way to do this?
myObject
Instead of any, you can use keyof to tell typescript that labels is an array of strings where strings are the keys of your interface:
Playground Link
interface CustomObject {
prop1: any;
prop2: any;
prop3: any;
}
const myObject: CustomObject = {
prop1: 1,
prop2: 2,
prop3: 3,
}
const labels: (keyof CustomObject)[] = ['prop1', 'prop2'];
const myArray = labels.map(label => ({ label, value: myObject[label] }))
console.log(myArray)
You can use Object.entries function to iterate over key/value pairs:
const myObject = { prop1: 'value1', prop2: 'value2', prop3: 'value3' }
let myArray = [];
Object.entries(myObject).map((item)=>{ myArray.push({label:item[0], value:item[1]})})
console.log(myArray)

Most efficient way to constrain parameter type based on another parameter

I have a list of car brands, I have a car factory that takes a car and an options object that differ depending on the brand.
type CarBrand = 'mazda' | 'toyota' | 'hyundai';
export function CarFactory(car: 'mazda', options: { a: string; b: string }): object;
export function CarFactory(car: 'toyota', options: { b: string; c: string; d: string }): object;
export function CarFactory(car: 'hyundai', options: { e: string; f: string; g: number }): object;
export function CarFactory(car: CarBrand, options: any): object {
return options;
}
This works for me at the moment, but if I have more car brands in the future it would be rather laborious adding new type signatures to overload the function. Is there a more efficient way of doing this?
I'd be inclined to make an interface representing the mapping from brand name to options, and then make your CarFactory function generic. Like this:
interface CarBrandOptions {
mazda: { a: string; b: string };
toyota: { b: string; c: string; d: string };
hyundai: { e: string; f: string; g: number };
}
type CarBrand = keyof CarBrandOptions;
function CarFactory<K extends CarBrand>(car: K, options: CarBrandOptions[K]): object {
return options;
}
This will act the same way that your current code does, I think:
CarFactory("mazda", { a: "", b: "" }); // okay
CarFactory("toyota", { a: "", b: "" }); // error!
// Argument of type '{ a: string; b: string; }'
// is not assignable to parameter of type '{ b: string; c: string; d: string; }'.
CarFactory("toyota", { b: "", c: "", d: "" }); //okay
but will make it easier to add more car brands by adding properties to or merging properties into the CarBrandOptions interface. Okay, hope that helps; good luck!
Link to code

Can we assign type to a object declared in a variable(modular Programming)

Hello I'm trying to assign a type to all the properties(keys) of an object which is declared inside a variable's definition
I've tried declaring the object outside as another variable like below code
interface d{
name: string;
id: number;
}
var newObject: d={
name: 'sam',
id: 1;
}
But I want to declare this object inside the variable a which i want to call somewhere else
Ex:
interface d{
name: string;
id:number;
}
var a={
newObject: d: {
name:'sam',
id: 1
}
}
You can't declare a type of a property inside an object literal.
You could declare the type on a, without needing a dedicated interface:
interface d{
name: string;
id:number;
}
var a: { newObject: d }={
newObject: {
name:'sam',
id: 1
}
}
Play
You could use a type assertion, but that will not be as type safe as it will disable some checks:
interface d{
name: string;
id:number;
}
var a ={
newObject: {
name:'sam',
id: 1,
randomProp : "" // no err
} as d
}
Play
If you need to do this often you might consider a helper function with a generic type parameter, this will be fully typesafe and avoid the extra type as in the first solution:
interface d {
name: string;
id: number;
}
function checked<T>(o: T) { return o; }
var a = {
newObject: checked<d>({
name: 'sam',
id: 1,
randomProp: "" // err
})
}
Play
Edit Answering the question in the comments, if you want to use this for an array, you can pretty much do the same:
Helper function
interface d {
name: string;
id: number;
}
function checked<T>(o: T) { return o; }
var a = {
newObject: checked<d[]>([{
name: 'sam',
id: 1,
}])
}
a.newObject.push({
id: 2,
name: ""
});
play
Type Assertion (ok if array is empty)
interface d{
name: string;
id:number;
}
var a ={
newObject: [] as d[]
}
a.newObject.push({ id: 1, name: "sam"})
play

Categories

Resources