typescript - Fördelar med att definiera typer i typdefinitionsfiler jämfört med i en vanlig typfil
För applikationskod, är det lämpligt att lägga Redux store-typer i state-slice.d.ts-filer?
Jag ärvde en kodbas där alla kärntyper har placerats i .d.ts-definitionsfiler, men detta strider mot hur jag har kommit att förstå definitionfils roll.
Min förståelse är att definitionsfiler är för när du skriver ett bibliotek (vilket är en bekväm plats för författare att centralisera ett gränssnitt för att definiera vårt biblioteks API som vi exponerar för användare), men de är olämpliga för applikationskod eftersom data kan förändras snabbt.
Det sagt, jag har inte kunnat komma på ett bra argument mot .d.ts-filer i sig - från ett funktionellt perspektiv ser jag ingen skillnad i att referera till en typ
declare namespace Domain {
interface State {
// ...
}
}
från en definitionsfil, eller genom att uttryckligen importera en fil från en fil som har
export interface ReduxState {}
För att lägga till lite mer kontext, eftersom detta är en lite ovanlig fråga - jag arbetar för närvarande på ett nytt projekt som behöver dela dessa kärntyper från den befintliga kodbasen jag ärvde, och jag försöker flytta dessa typer till ett separat paket.
Tyvärr har jag stött på problem med att importera och exportera dessa typer - det verkar som att jag måste (notera exports)
/* package A */
export declare namespace StateA { }
/* package B */
import { StateA } from 'packageA/StateA'
const stateA: StateA = {};
istället för att TypeScript automatiskt plockar upp det.
/* package B (originally) */
// stateA.d.ts
declare namespace StateA {}
// test.ts
const stateA: StateA = {};
Jag har försökt ändra fältet typeRoots i tsconfig.json-filen för att se om jag kan undvika att behöva infoga import-satser överallt, men utan framgång.
Jag tänkte att detta kanske kunde vara ett bra tillfälle att konvertera alla typdefinitionsfiler till vanliga typfiler, men jag kan helt enkelt inte komma på någon bra anledning förutom "Jag tycker inte att vi borde använda typdefinitionsfiler" och "Jag kan inte få typerna i definitionsfilerna att fungera som de gör nu".
Så mina frågor är
- Vilka är fördelarna med att använda typdefinitionsfiler jämfört med att definiera typer i en vanlig
.ts-fil? - Bör vi skriva våra egna definitionsfiler när vi arbetar med kod för applikationer som riktar sig till konsumenter?
- Tänker jag för mycket och det spelar egentligen ingen roll?
Jag ber om ursäkt i förväg om denna fråga är lite öppen, jag hoppas att detta inte är olämpligt för StackOverflow-communityn (eller om det är det, vilket skulle vara ett bra forum att fråga på?). Tack!
Svar #1
Har du sett detta svar Hur använder jag namnrymder med TypeScript externa moduler?
Jag håller inte helt med om det, men jag tycker att det är värt att ta en titt. Jag ställde mig nyligen alla de där frågorna också.
Här är vad jag har gjort:
För typer som bara används inom en fil eller några få filer definierar och exporterar jag dem från vanliga ts-filer.
Till exempel:
store.ts
Den här filen definierar en APP_THUNK-typ som bara används av några filer som hanterar skapandet av redux-thunks. Så jag definierar och exporterar från den här filen, eftersom den använder ROOT_STATE-typen som också är definierad här.
import { configureStore } from "@reduxjs/toolkit";
import { rootReducer } from "./rootReducer";
import { Action } from "@reduxjs/toolkit";
import { ThunkAction } from "redux-thunk";
export interface ROOT_STATE {
...
}
export type APP_THUNK = ThunkAction<
Promise<void> | void,
ROOT_STATE,
unknown,
Action<string>
>
export const store = configureStore({
reducer: rootReducer
});
Sedan, när jag behöver använda de typerna, importerar jag dem: import { APP_THUNK } from "@redux/store";
Men jag har också vissa typer som används i hela mitt projekt, i många filer. Och för den typen av typer behåller jag d.ts-filer och gör dem tillgängliga genom att använda declare namespace SOME_NAMESPACE.
Till exempel:
BLOGPOST.d.ts
Denna fil definierar alla typer som är relaterade till blogPost-objekt.
declare namespace PROJECT {
interface BLOGPOST {
id: string,
slug: string,
images: BLOGPOST_IMAGE[]
}
interface BLOGPOST_IMAGE {
id: string,
src: string,
alt: string,
caption: string,
}
// AND SO ON...
// THERE ARE MANY MORE TYPES HERE RELATED TO BLOGPOSTS
}
Jag slår också ihop PROJECT-namnrymden från olika filer. Till exempel: PRODUCTS.d.ts deklarerar också namespace PROJECT och den slås automatiskt ihop med den på BLOGPOSTS.d.ts.
Och att använda de typerna är mycket bekvämt:
interface Props {
blogPost: PROJECT.BLOGPOST
}
// OR MAYBE
const blogPost = await API.getBlogPost() as PROJECT.BLOGPOST;
Och autokomplettering blir omedelbart grupperad under det namnutrymmet. Vilket jag också tycker är mycket trevligt.
Detta fungerar för mig. Vänligen ta hänsyn till att jag arbetar ensam och dessa typer används endast inom detta enda projekt. Vet inte om detta mönster skulle fungera för team med flera personer eller över olika projekt.
Observera också att i Typescripts officiella dokumentation får vi det:
Använda moduler
Moduler kan innehålla både kod och deklarationer.
Moduler har också ett beroende av en moduluppladdare (som CommonJs/Require.js) eller en runtime som stöder ES-moduler. Moduler möjliggör bättre kodåteranvändning, starkare isolering och bättre verktygsstöd för paketering.
Det är också värt att notera att, för Node.js-applikationer, är moduler standard och vi rekommenderar moduler framför namnrymder i modern kod.
Från och med ECMAScript 2015 är moduler en inbyggd del av språket och bör stödjas av alla kompatibla motorimplementationer. Därför skulle moduler för nya projekt vara den rekommenderade mekanismen för kodorganisering.
Men jag tycker fortfarande att när du använder dessa typer är det en trevligare utvecklarupplevelse när du använder namespaces istället för moduler. Men var medveten om kolliderande namespaces, med tanke på att de är globala.
