Можно ли при создании объявлений машинописного текста для библиотеки javascript выполнять циклический импорт в файлах .d.ts? Например.:
файл a.d.ts
import { B } from "./b";
export class A { ... }
...
файл b.d.ts
import { A } from "./a";
export interface B { ... }
...
Насколько я понимаю, они используются только для компиляции. Предположим, что в библиотеке javascript нет циклических зависимостей. До сих пор я не обнаружил никаких проблем при использовании объявлений, подобных приведенным выше, в тестовой программе. Мой вопрос в том, является ли такая практика нормальной или считается плохим дизайном, которого следует избегать? Я понимаю, что тот же подход вызовет проблемы во время выполнения (неопределенный экспорт и т. д.), но во время компиляции кажется, что компилятор машинописного текста может их решить (возможно, путем объединения всех объявлений в один модуль?). Или я что-то упустил, и этой практики следует избегать? Если да, можете ли вы привести пример, когда это может вызвать проблему при использовании таких файлов .d.ts?
Когда вы создаете объявления для существующих библиотек JS, вам не нужно заботиться о том, хорошо это или плохо, просто введите их как есть. TS был создан, чтобы охватить как можно больше JS, поэтому, если он работает в JS, он, вероятно, будет работать и в TS.
Циклические ссылки в типах очень распространены и иногда неизбежны, например.
// `parent` can't be just a common interface because exact typings are needed and and DRY
class A {
parent?: A | B
}
class B {
parent?: A | B
}
или циклические дженерики
class A<T> { b?: B<T> }
class B<T> { a?: A<T> }
так что они в порядке. Что не в порядке, так это то, что вы используете полный импорт. Вы должны использовать
import type { A } from './a'
// ^^^^ says it doesn't actually imports that in runtime
commonjs
.// e.g. used in both A and B classes
function makeAorB(x: boolean): A | B { return x ? new A() : new B() }
Примером неудачного создания экземпляра может быть zod TypeError: Cannot read properties of undefined (чтение '_parse')
Итак, в вашем примере в 1 было бы нормально, если бы классы A и B были объявлены в отдельных файлах .d.ts и экспортированы, но внутри этих файлов они должны быть импортированы через «тип импорта»?
@YevgeniyP правильно было бы следовать источнику JS. Если './a' ИМПОРТИРУЕТ import './b'
или import {...} from './b'
во время выполнения, это должно быть import
. Если './a' не import './b'
во время выполнения, это должно быть import type
. т.е. так же, как это было бы в файле TS, который мог бы сгенерировать этот d.ts
Что, если некоторые или все эти файлы содержат только объявления, используемые во время компиляции (например, интерфейсы и типы), поэтому для них нет исходного кода JS? Если я использую в них «тип импорта», то использование кругового импорта в этих файлах не будет проблемой?
Да. Круговой import type
— это хорошо, круговой import
— нормально, а плохой — это циклический импорт только во время выполнения, ЕСЛИ он немедленно приводит к сбою приложения. Если это не крашит приложение, все в порядке.
Попробуй это. Если это работает, то это работает. Если это не так, вам нужно либо изменить то, где у вас есть вещи, либо переосмыслить свои типы.