Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TypeScript : avoid extra properties

Tags:

typescript

To make it simple, I have an issue with Typescript's "Excess Property Checks" behavior. I would like to be sure that an object with extra properties is not be accepted by TypeScript.

In my example of a simple interface I could simply pick the available data, but I have a lot of properties and I would like to avoid filtering them on runtime, is there a way?

Here you can find an example code I made for this topic :

TypeScript Playground

type LayoutType {
    margin: number;
}

const badData = {
    margin: 23,
    padding: 23,
}

function func(param: LayoutType) {
    console.log(param);
    // Here I want to use property being sure param only contains LayoutType property
}

// OK
func({ margin: 42 })
// OK : padding is detected as unwanted property
func({ margin: 42, padding: 32 })
// KO : bad data shouldn't fit
func(badData)

/* SAME */

// OK : padding is detected as unwanted property
const test1: LayoutType = { margin: 42, padding: 32 };
// KO : bad data shouldn't fit
const test2: LayoutType = badData;
like image 559
Exayy Avatar asked May 22 '20 17:05

Exayy


People also ask

Can you restrict TypeScript object to contain only properties defined by its class?

Typescript can't restrict extra properties Unfortunately this isn't currently possible in Typescript, and somewhat contradicts the shape nature of TS type checking.

How do I get all properties optional TypeScript?

Use the Partial utility type to make all of the properties in a type optional, e.g. const emp: Partial<Employee> = {}; . The Partial utility type constructs a new type with all properties of the provided type set to optional. Copied!

What is TypeScript never?

TypeScript introduced a new type never , which indicates the values that will never occur. The never type is used when you are sure that something is never going to occur. For example, you write a function which will not return to its end point or always throws an exception.

What is interface TypeScript?

Interface is a structure that defines the contract in your application. It defines the syntax for classes to follow. Classes that are derived from an interface must follow the structure provided by their interface. The TypeScript compiler does not convert interface to JavaScript.


1 Answers

Sounds like you want an Exact type. Typescript doesn't come with one, but it's easy to make:

type Exact<A, B> = A extends B
  ? B extends A
    ? A
    : never
  : never

This basically says that if and A extends B and B extends A then the types are identical, neither are a subset or superset of the other. So it should allow that type through. If they are not identical, then the type is never which prevents that type from being allowed.

Now you just need to make your function generic, and enforce that argument to exactly the right type:

function func<T>(param: Exact<T, LayoutType>) {
    console.log(param);
}
func(badData)
// Argument of type '{ margin: number; padding: number; }'
//   is not assignable to parameter of type 'never'.

Playground

More relevant reading here in Typescript issue #12936


Lastly, the reason that an object literal doesn't work, but an object variable does is that the literal is being constructed for a particular type. Typescript can't know know about the extra properties because there is no type information for those properties. So the typescript program can't use those properties, because they aren't declared to exist.

However, when the object is a variable and the extra properties are known, then it is a separate but compatible type. The extra properties may not be used in a function that only accepts the narrow type, but in other code that knows about the wider type the properties can be used.

This is why this is valid:

const obj1 = { a: 1, b: 2 }
const obj2: { a: number } = obj1

console.log(obj1.b) // b can still be accessed

But this is not:

const obj1: { a: number } = { a: 1, b: 2 }
//                                  ^ type error
like image 102
Alex Wayne Avatar answered Dec 19 '22 01:12

Alex Wayne