Почему typescript игнорирует строгие проверки null при реализации абстрактных функций?

#typescript #types #null #nullable #typescript-types

#typescript #типы #null #nullable #typescript-типы

Вопрос:

Рассмотрим следующий абстрактный класс:

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

Изменение параметра на ненулевой в конкретной реализации не приводит к ошибке типа. Это неожиданно.

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

Это позволяет мне делать:

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

Почему это возможно?

Включены строгие проверки null. Версия Typescript — 4.5.3.

Ответ №1:

Типы параметров метода проверяются двумерно, поэтому, пока типы параметров реализации и абстрактной подписи связаны в любом направлении, он будет проверять тип (в данном случае string это подтип string | null )

Вы могли бы использовать синтаксис подписи функции, чтобы избежать этого, но только для интерфейсов (производные классы не могут реализовывать члены функции как методы, к сожалению, ex):

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

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

Ссылка на игровую площадку

В настоящее время нет флага для изменения этого поведения (начиная с TS 4.6). Вы можете немного прочитать о причинах этого решения в PR, который представил strictFunctionTypes

Если вы хотите узнать больше о том, что такое дисперсия и как она работает в typescript, вы можете ознакомиться с моим докладом на эту тему