I would like to define my interface so that when you give a nav that is a navigationItem, you can optionally give childs for a dropdown. when the childs property is given I would like to enforce the icon property of the navigationItem.
When no childs property are given the icon property should not be given.
Any idea on how I could achieve that ?
this is my current interface
interface dropdown {
name: string;
href: string;
icon: IconDefinition;
}
interface navigationItem {
name: string;
href: string;
icon: IconDefinition; // only needed if the childs is given
childs?: dropdown[];
}
You should be able to do this using two different Interfaces like this.
Have one with
interface Type1 {
name: string;
href: string;
childs: never;
}
The second one will be
interface Type2 {
name: string;
href: string;
childs: dropdown[];
icon: IconDefinition;
}
Then you can use both the types using a conditional like this
value: Type1 | Type2
Why this works is, when you add icon: never to Type1, you can only have name and href keys.
So if someone adds the childs key, then the type becomes Type2 which needs to have icon given as well.
So, if const a: Type1 | Type2 = { name: '', href: '', childs: [] };, it throws
Type '{ name: string; href: string; childs: []; }' is not assignable to type 'Type1 | Type2'.
Property 'icon' is missing in type '{ name: string; href: string; childs: dropdown[]; }' but required in type 'Type2'.
If you want to use a single type instead, define a new type like this.
type Type3 = Type1 | Type2;
Then use it like this
const a: Type3;
Related
Given the following class:
export class condition {
attribute: string;
operator: string;
value: (string | any[]);
uitype: string;
valueof: string;
operationTypeAttributeTypeCode: AttributeTypeCode;
attributeValueHistory: attributeValueHistory[];
}
for the value field, I want to determine if the type is of type any[] and if it is, peform a forEach on the array values
I have tried type casting such as (obj.value as any[]) != undefined or .length > 0 but cant ever seem to test for the type correctly.
Is there a reliable way to do this?
Thank you
Just use regular Array.isArray check, like here:
export class Condition {
attribute: string;
operator: string;
value: (string | any[]);
uitype: string;
valueof: string;
operationTypeAttributeTypeCode: AttributeTypeCode;
attributeValueHistory: attributeValueHistory[];
}
const instance = new Condition();
if (Array.isArray(instance.value)) {
instance.value.forEach(console.log)
}
1. Array of Strings -> Union of Strings (It works)
I saw a solution creates a union of string literals from an array of strings.
const keys = ["cat", "dog"] as const;
type Keys = typeof keys[number]; // "name" | "age"
2. Array of Strings -> Union of Objects (Is it possible?)
My question is if it's possible to create a union of objects from an array of strings? The code below works, I just want to shorten the SET_PROPERTY_PAYLOAD with something like keyof IObject and generate a union of object types from there.
interface IObject = {
id: string;
name: string;
age: number;
}
// Is it possible to shorten this type using typescript's primitives
type SET_PROPERTY_PAYLOAD =
| {
id: string;
propertyName: "id"; // keyof IObject at index 0
value: string; // IObject["id"]
}
| {
id: string;
propertyName: "name"; // keyof IObject at index 1
value: string; // IObject["name"]
}
| {
id: string;
propertyName: "age"; // keyof IObject at index 2
value: number; // IObject["age"]
};
My goal for this is to narrow the type of value by inferring from the current value of propertyName.
You want a mapped type.
interface IObject {
id: string;
name: string;
age: number;
}
type SET_PROPERTY_PAYLOAD = {
[K in keyof IObject]: {
id: string,
propertyName: K,
value: IObject[K]
}
}[keyof IObject]
This type maps over the keys in IObject and create a new object type for each key.
SET_PROPERTY_PAYLOAD should then be exactly equivalent to your long form version above.
See Playground
I have an interface for an element:
export interface iElm {
name?: string;
appearance?: string;
atomic_mass?: number;
boil?: number;
category?: string;
density?: number;
discovered_by?: string;
melt?: number;
molar_heat?: number;
named_by?: string;
number?: number;
period?: number;
phase?: string;
source?: string;
spectral_img?: string;
summary?: string;
symbol?: string;
xpos?: number;
ypos?: number;
shells?: number[];
electron_configuration?: string;
electron_configuration_semantic?: string;
electron_affinity?: number;
electronegativity_pauling?: number;
ionization_energies?: number[];
cpk_hex?: string;
}
I got this by using a utility similar to the one in this question (json to ts): How to convert a json to a typescript interface?
The json that I'm using is an object of element object that are all a bit different but one looks like this:
"helium": {
"name": "Helium",
"appearance": "colorless gas, exhibiting a red-orange glow when placed in a high-voltage electric field",
"atomic_mass": 4.0026022,
"boil": 4.222,
"category": "noble gas",
"density": 0.1786,
"discovered_by": "Pierre Janssen",
"melt": 0.95,
"molar_heat": null,
"named_by": null,
"number": 2,
"period": 1,
"phase": "Gas",
"source": "https://en.wikipedia.org/wiki/Helium",
"spectral_img": "https://en.wikipedia.org/wiki/File:Helium_spectrum.jpg",
"summary": "Helium is a chemical element with symbol He and atomic number 2. It is a colorless, odorless, tasteless, non-toxic, inert, monatomic gas that heads the noble gas group in the periodic table. Its boiling and melting points are the lowest among all the elements.",
"symbol": "He",
"xpos": 18,
"ypos": 1,
"shells": [2],
"electron_configuration": "1s2",
"electron_configuration_semantic": "1s2",
"electron_affinity": -48,
"electronegativity_pauling": null,
"ionization_energies": [2372.3, 5250.5],
"cpk_hex": "d9ffff"
}
As you can see everything lines up with the iElm interface (properties are optional because there are some weird corner case elements)
Now here's my problem: I have a React function component that takes in an iElm:
const Element: FC<iElm> = (props) => {
// some jsx stuff with the data
}
I can pass properties of the json to Element like so:
<Element name={table.boron.name}/>
But is there some workaround so that I don't have to go through every property one by one and copy it over?
You can simply use the spread operator to pass the entire object in:
<Element {...table.boron} />
They is the following code errors on the for loop with TS 2488 for the type of apps - which at the time is of type never?
If I remove the 3rd comparison in the if, Array.isArray(apps), then the the type is correct in the for loop (ILuisApp[]) instead of type never.
export interface ILuisFull {
id: string;
name: string;
description: string;
culture: string;
versionsCount: number;
app_createdDateTime: string;
endpointHitsCount: number;
activeVersion: string;
tokenizerVersion: string;
}
export interface ILuisApp {
id: string;
name: string;
description: string;
culture: string;
usageScenario: string;
domain: string;
versionsCount: number;
createdDateTime: string;
endpointHitsCount: number;
activeVersion: string;
ownerEmail?: string;
tokenizerVersion: string;
}
export const transform = (apps: ILuisApp[]): ILuisFull[] => {
if (!apps || apps.length === 0 || Array.isArray(apps)) return [] as ILuisFull[];
let fullTable: Array<ILuisFull> = [];
// apps is type never unless remove 3rd comparison in if above
for (var val of apps) {
console.log(val);
}
return fullTable;
}
ts config includes:
"target": "es5",
"module": "commonjs",
"lib": [
"es2016",
"es2017.object",
"esnext.asynciterable"
],
It looks like compiler thinks that apps is not initialized. It thinks that arr is null. So it says:
Type 'never' must have a 'Symbol.iterator' method that returns an
iterator.
However, it is possible to use casting apps as ILuisApp[]) to say what type of apps is:
for (let val of apps as ILuisApp[]) {
console.log(val);
}
I have an object in state and I need to dynamically access its values inside the input component,
var selectedLangTab has a number type
I am trying to get a value by following:
value={userInput.translations[selectedLangTab].title
Error:
Element implicitly has an 'any' type because type '{ 0: { title: string; additionalInfo: string; content: string; language: Language; }; 1: { title: string; additionalInfo: string; content: string; language: Language; }; }' has no index signature.
translations: {
[Language.CS]: {
title: 'Title CZ',
additionalInfo: '',
content: '',
language: Language.CS
},
[Language.EN]: {
title: 'Title EN',
additionalInfo: '',
content: '',
language: Language.EN
}
}
interface of translations:
translations: {
[key in Language]: {
title: string;
additionalInfo: string;
content: string;
language: Language;
}
};
interface of Language:
export enum Language {
CS,
EN
}
The problem is that translations has the two properties (0 and 1) that come from the enum if you are indexing with selectedLangTab which is number there is no guarantee that the number will be in this range, hence the error.
You can defined selectedLangTab to be of type 0 | 1 or Language to make the error go away:
let selectedLangTab: 0 | 1 = 1;
userInput.translations[selectedLangTab].title
Playground link
Or you can use a type assertion to tell the compiler you are sure the number is in teh appropriate range:
let selectedLangTab: number = 1;
userInput.translations[selectedLangTab as Language].title
Playground link