Home > OS >  Is there a way to have a loose interface with SOME strictness?
Is there a way to have a loose interface with SOME strictness?

Time:02-08

I was hoping I could have an interface like:

interface Item {
  [ index: string ] : string | number | Item,
  id: number,
}

So that an Item, can have any properties with string/number/object values, BUT-- if an id property is used, it MUST be a number.

Is this possible?

EDIT

I originally asked this, but then tested it out and what I did seems to work fine, UNLESS I am using optional attributes...

If I do this, then I get a typescript error:

interface Item {
  [ index: string ] : string | number | Item,
  id?: number,
}

In order to get this to work, I have to do:

interface Item {
  [ index: string ] : string | number | Item | undefined,
  id?: number,
}

Which I don't think I want all my other properties to be potentially undefined... Is there another way to have id be optional?

EDIT AGAIN

Turns out even with the | undefined this doesn't work.. If I do the following:

let i : Item = { id: 123, foo: { bar: { id: 555} } };
console.log(i.foo.bar.id);

I get errors about "object is possibly undefined"

CodePudding user response:

According to the doc, you can as long as the type of id is a subtype of [index: string]

interface NumberDictionary {
  [index: string]: number;
     
  length: number; // ok
  name: string; // not ok
  // Property 'name' of type 'string' is not assignable to 'string' index type 'number'.
}

interface NumberOrStringDictionary {
  [index: string]: number | string;
  length: number; // ok, length is a number
  name: string; // ok, name is a string
}

Edit:

I'm not sure if this is the best way but this seems to work

interface Item1 {
  [ index: string ] : string | number | Item;
}

interface Item2 {
  id?: number;
}

type Item = Item1 & Item2

Also "object is possibly undefined" error can be solved with optional chaining

let i : Item = { id: 123, foo: { bar: { id: 555} } };
console.log(i.foo?.bar?.id);

CodePudding user response:

Answer already provided by "noob" but extending a Record seems to work fine:

https://www.typescriptlang.org/play?#code/C4TwDgpgBAKhDOwoF4oCUIGMD2AnAJgDyK4CWAdgOYA0UJFlAPuQK4C2ARhLgHxQBkUAN4AoKOKil8AfgBcUVp24BuEQF9VIgDYQkwAAzy4iFMLESAZtmzyAjNXPiOAQ1zyARACZ36kdt1QwLZGCEioohKS B627g6RVjZQ9o5QLm5QXj5qfjp6niEm4alSdvGW1mWp6R7eviJAA

type Test = Record<string, string|number> & {
    id?: number;
};

let t0: Test = {
    foo: 1,
    bar: "2"
}

let t1: Test = {
    id: "1",
    foo: 1,
    bar: "2"
}

let t2: Test = {
    id: 1,
    foo: 1,
    bar: "2"
}

You will see an error:

Errors in code Type 'string' is not assignable to type 'number | undefined'.

  •  Tags:  
  • Related