Home > Blockchain >  Why does typescript disregard strict null checks when implementing abstract functions?
Why does typescript disregard strict null checks when implementing abstract functions?

Time:12-11

Consider the following abstract class:

export abstract class Foo {
  abstract bar(param: string | null): string
}

Changing the parameter to be non-nullable in a concrete implementation does not cause a type error. This is unexpected.

export class ConcreteFoo extends Foo {
  bar(param: string): string {
    return param
  }
}

This allows me to do:

const inst = new ConcreteFoo() as Foo
const res = inst.bar(null)
console.log(res) // res is null, but typescript says it is string

Why is this possible?

Strict null checks are on. Typescript version is 4.5.3.

CodePudding user response:

Method parameter types are checked bivariantly, so as long as the parameters types of the implementation and the abstract signature are related in either direction it will type check (in this case string is a sub type of string | null)

You could use function signature syntax to avoid this but only for interfaces (derived classes can't implement function members as methods unfortunately ex):

export interface Foo {
  bar: (param: string | null) => string
}

export class ConcreteFoo implements Foo{
  bar(param: string /* | null */): string { // error
    return param!
  }
}

Playground Link

There is currently no flag to change this behavior (as of TS 4.6). You can read a little bit about the reasoning behind this decision in the PR that introduced strictFunctionTypes

If you want to learn more about what variance is and how it works in typescript you can check out my talk on the topic

  • Related