打字稿:输入对象具有接口键的子集

Lio*_*Tay 3 types typescript

我希望能够指定函数的输入对象应仅包含现有接口中存在的键(它不需要具有所有键)。

interface Group {
  name: string
}

interface Person {
  id: number
  name: string
  email: string
  age: number
}

interface AddedProperties {
  groups: Group[]
}

function createPerson<D extends object>( // <-- Should convey, D is an object with some keys of Person
  personData: D
): D & AddedProperties {
  return Object.assign({}, personData, { groups: [] })
}

interface NameAndAge {
  name: string
  age: number
}

// Valid: Should be valid
const person: NameAndAge = createPerson({ name: "bob", age: 5 })
// Valid: Should be invalid as 'personality' doesn't exist on Person
const person2 = createPerson({ name: "bob", age: 5, personality: "cheerful" })
Run Code Online (Sandbox Code Playgroud)

打字稿可能吗?

Ing*_*ürk 8

一个简单的方法是使用Partial<>

function createPerson(
  personData: Partial<Person>
): Partial<Person> & AddedProperties {
  return { ...personData, groups: [] };
}
Run Code Online (Sandbox Code Playgroud)

Partial 采用一个类型并使其所有成员可选,从而允许您指定该类型的任何属性子集。

这种类型的缺点是返回的类型不知道你输入了什么:

createPerson({name: "Bob", age: 5});         // OK
createPerson({name: "Bob", gender: "male"}); // Type error
createPerson({name: "Bob"}).name;            // OK
createPerson({name: "Bob"}).age;             // OK :-(
Run Code Online (Sandbox Code Playgroud)

如果您也想避免这种情况,请查看我的其他答案。


Ing*_*ürk 5

好的,经过一些研究,我现在找到了更好的答案:-)不过,我也将保留其他答案。您可以执行以下操作:

function createPerson<D extends keyof Person>(
    personData: Pick<Person, D>
): Pick<Person, D> & AdditionalProperties {
    return Object.assign({}, personData, {groups: []});
}
Run Code Online (Sandbox Code Playgroud)

personData仅是的严格子集Person,并且返回类型将完全相同,结构加上AdditionalProperties

createPerson({name: "Bob", age: 5});         // OK
createPerson({name: "Bob", gender: "male"}); // Type error
createPerson({name: "Bob"}).name;            // OK
createPerson({name: "Bob"}).age;             // Type error
Run Code Online (Sandbox Code Playgroud)

它是如何工作的?泛型D extends keyof Person将再次允许添加新字段,这是我们要避免的。我们不能在泛型类型参数的定义中处理此问题(因为我们只有extends那里),而是将实际函数参数限制为

personData: Pick<Person, D>
Run Code Online (Sandbox Code Playgroud)

这将输入限制为仅Person出现在中的那些属性D。我们使用与返回类型相同的类型(与交叉AdditionalProperties)。


您可以在这里查看它的运行情况: