Dmytro Filipenko
Dmytro Filipenko

Reputation: 941

Generic in function doesn't check argument type in Typescript

I'm expecting that typescript will throw an error because I'm passing the wrong number of elements in EntryPoints, but it doesn't happen.

function createContext<T>(defaultValue: T): T[] {
  return [defaultValue]
}

interface EntryPoints {
  parentSelector: string;
}
interface SomeType {
  entryPoints: EntryPoints[];
}
const defaultData = {
  entryPoints: [{
    parentSelector: '',
    foo: 1 // <-- expecting error here
  }]
}
createContext<SomeType>(defaultData)

Same code without generic works as expected

function createContext<T>(defaultValue: T): T[] {
  return [defaultValue]
}

interface EntryPoints {
  parentSelector: string;
}
interface SomeType {
  entryPoints: EntryPoints[];
}
const defaultData: SomeType = {
  entryPoints: [{
    parentSelector: '',
    foo: 1 // <-- throwing error here
  }]
}
createContext(defaultData)

Playground

Upvotes: 3

Views: 57

Answers (2)

Shaun Luttin
Shaun Luttin

Reputation: 141442

What you're experiencing is the difference between

  1. type checking at the point of assigning a literal to a type and
  2. type checking at the point of assigning a variable to a type.

Consider that we have two types. One of them has one more property than the other has.

type Foo = {
  foo: string;
};

type FooBar = {
  foo: string;
  bar: string;
};

When assigning an object literal to a type, additional properties are NOT allowed.

// Object literal may only specify known properties,
// and 'bar' does not exist in type 'Foo'.
const foo: Foo = {
  foo: "foo",
  bar: "bar" // <---- bar is not allowed on Foo.
};

When assigning a variable to a type, additional properties are allowed.

const fooBar: FooBar = {
  foo: "foo",
  bar: "bar" // <---- bar is going to be allowed on Foo
};

const foo: Foo = fooBar; // <---- see, no error

It is okay to assign fooBar to foo because fooBar is a variable not an object literal and can therefore include unknown properties.

Upvotes: 1

skovy
skovy

Reputation: 5650

You're running into an object freshness issue so the additional key is allowed.

If you explicitly pass the object it will be properly typed checked:

function createContext<T>(defaultValue: T): T[] {
  return [defaultValue];
}

interface EntryPoints {
  parentSelector: string;
}

interface SomeType {
  entryPoints: EntryPoints[];
}

createContext<SomeType>({
  entryPoints: [
    {
      parentSelector: "",
      foo: 1 // <-- errors as expected here
    }
  ]
});

TypeScript Playground

Upvotes: 2

Related Questions