I find type definitions to be quite intricate, with a lengthy evolutionary background.
When using node_modules/@types
, it acts as a pre-established "type root" for TypeScript to locate definitions when importing non-relative modules (such as "chai"
being non-relative and "./chai"
being relative). TypeScript looks in node_modules/@types
for resolution. For instance, from "chai"
leads TypeScript to discover
node_modules/@types/chai/index.d.ts
for the definition. This is how TypeScript determines what can be imported from that module and their structures.
You can learn more about module resolution from the documentation.
The structure of a .d.ts
file can sometimes be challenging to comprehend (especially because most published libraries support various module system flavors simultaneously). In short, chai exports an interface ChaiStatic
describing the module's structure which includes a member expect
of type ExpectStatic
. Hence, TypeScript knows what import { expect } from "chai"
entails.
It's also worth noting that Chai's current definitions do not exactly align with the most recent method of defining a UMD module. However, this does not render them incompatible in any manner.