Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Typescript: Input object has subset of keys of an interface

I want to be able to specify that an input object to a function should only contains keys that are present in an existing interface (it does not need to have all the keys).

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" })

Is this possible in typescript?

like image 740
Lionel Tay Avatar asked Jul 22 '18 06:07

Lionel Tay


People also ask

Can we have an interface with optional properties in TypeScript?

You must tell TypeScript if a property is optional. First, if you don't tell TypeScript that a property is optional, it will expect it to be set. Adding ? to the property name on a type, interface, or class definition will mark that property as optional.

Are interfaces objects TypeScript?

TypeScript Interface Define Objects Only In TypeScript, type aliases can define composite types such as objects and unions as well as primitive types such as numbers and strings; interface, however, can only define objects. Interface is useful in typing objects written for object-oriented programs.

What is key string in TypeScript?

The {[key: string]: string} syntax is an index signature in TypeScript and is used when we don't know all the names of a type's properties ahead of time, but know the shape of the values. The index signature in the examples means that when an the object is indexed with a string , it will return a string .


1 Answers

A simple approach is using Partial<>:

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

Partial takes a type and makes all of its members optional, thus allowing you to specify any subset of properties of that type.

The downside of this typing is that the returned type has no knowledge of what you put into it:

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

If you want to avoid this as well, check out my other answer.

like image 92
Ingo Bürk Avatar answered Oct 26 '22 12:10

Ingo Bürk