固定号码动态命名键的对象接口

时间:2017-08-16 16:19:39

标签: typescript

如何为这些对象定义接口:

let bob = {
  Bob: {
    age: 9,
    gender: 'M'
  }
};
let alice = {
  Alice: {
    age: 12,
    gender: 'F'
  }
};

我可以使用来自this thread的解决方案,但它过于宽松,我想将该用例限制为唯一(或固定数量)的密钥。

BONUS 如果你能弄清楚如何表达:

  • 顶级对象中允许的密钥数
  • 为每个键指定不同的接口

像这样的伪代码:

interface funnyEdge {
    <string>from: fromEdge,
    <string>to: toEdge
}
let edge: funnyEdge = {foo: {...}, bar: {...}}

4 个答案:

答案 0 :(得分:2)

TypeScript中的类型系统不允许您指定具有指定数量的未指定键的类型。实际上,由于它缺少exact types,因此TypeScript实际上不允许您指定具有指定数量的指定键的类型。它只对对象文字进行过多的属性检查。作为一种类型,如:

type Person = {
  age: number,
  gender: 'M' | 'F' | 'O'
}

并不意味着Person不能拥有其他密钥,例如:

const pirate = {
  age: 35,
  gender: 'M' as 'M',
  legs: 1,
  parrot: true
};
const piratesArePeopleToo: Person = pirate; // no problem

所以你最希望的是,如果传入一个包含太多键的对象文字,TypeScript会抱怨,如:

const otherPirate: Person = {
  age: 40,
  gender: 'F',
  eyes: 1,
  parrot: false
}; // error

不,就我所知,TypeScript不会让你表达这种约束。

我能想到的最好的解决方法是:创建一个带有单个键和Person的函数,并返回所需类型的对象:

type NamedPerson<K extends string> = Record<K, Person>;
function makeNamedPerson<K extends string>(key: K, person: Person): NamedPerson<K> {
  let namedPerson = {} as NamedPerson<K>;
  namedPerson[key]=person;
  return namedPerson;
}
const namedPerson = makeNamedPerson('chris', { age: 10, gender: 'O' });
console.log(namedPerson.chris.age);

如果组织代码,那么获取类型NamedPerson<K>的对象的唯一合理方法是调用函数makeNamedPerson()(例如,仅导出函数并生成它生成类的类型一个private成员),然后你知道每个人都有一个密钥。这对于你来说并不像类型系统那样强制实施,但至少你使你的库用户难以规避约束。

希望有所帮助;祝你好运!

答案 1 :(得分:1)

喜欢这个吗?

interface Person {
  age: number;
  gender: "M" | "F";
}

interface Bob extends Person { 
  //...
}

interface Alice extends Person { 
  //...
}

interface Members {
  Bob?: Bob;
  Alice?: Alice;
}

let bob: Members = {
  Bob: {
    age: 9,
    gender: 'M'
  }
};
let alice: Members = {
  Alice: {
    age: 12,
    gender: 'F'
  }
};

alert(alice.Alice.age) // ok
alice.Bob = bob.Bob // ok
alert(alice.Eve.age) // error

Try it in the Playground

答案 2 :(得分:1)

这个怎么样?

type Person<TName extends string> = {
  [key in TName]: {
    age: number,
    gender: 'M'|'F'
  }
};

var alice: Person<"Alice">;
alice.Alice.age = 42;

alice.AnythingElse // error;

多个键:

var johnAndMary: Person<"John" | "Mary">;
johnAndMary.John.age;
johnAndMary.Mary.gender;
johnAndMary.Don // error

答案 3 :(得分:0)

原来现在有办法做到这一点:

type Peeps = 'Bob'|'Alice';
type Def = { ... };
type Person = {[name in Peeps]?: Def }
// or if you're so inclined
type Person = Map<Peeps,Def>

// here with the other example
type EdgeEnd = 'from'|'to';
type FunnyNode = { ... };
type FunnyEdge = {[end in EdgeEnd]: FunnyNode};