TypeScript泛型类构造器参数类型守卫触发ts(4063)错误求助
Let's break down what's going wrong here and how to fix it.
The Root Cause of the Error
Your constructor parameter type C extends Converters is invalid TypeScript syntax. C extends Converters is a conditional type check, not a valid type annotation for a parameter. TypeScript can't parse this correctly, which leads to the confusing "private name" error (it's a compiler misinterpretation of the invalid syntax, not an issue with unexported types).
Your goal is clear:
- Allow the constructor to optionally accept a
Convertersinstance - Have the generic type
Creflect whether a converter was provided (to drive theNamesandValuetypes)
The Correct Approach
We need to adjust the generic constraint and constructor parameter type to properly link the input to the generic type. Here's the fixed implementation:
import { Converter } from "./characteristic"; export { Converter } from "./characteristic"; export type Converters = Record<string, Converter<any>>; export type Names<C> = C extends Converters ? keyof C : string | number; export type Value<C, N extends Names<C>> = C extends Converters ? ConvertersValue<C, N> : Buffer; export type ConvertersValue< C extends Converters, N extends Names<C> > = C[N] extends Converter<infer V> ? V : never; export default class Service<C extends Converters | undefined = undefined> { private converters?: Converters; // Constructor now accepts a parameter of type C (either Converters or undefined) constructor(converters?: C) { this.converters = converters; } public async read<N extends Names<C>>(name: N): Promise<Value<C, N>> { throw new Error("Not implemented yet"); } }
Key Changes Explained
Generic Constraint & Default Value:
We setC extends Converters | undefined = undefinedto:- Restrict
Cto either aConvertersobject orundefined - Provide
undefinedas the default type when no converter is passed, which triggers your fallback typings forNamesandValue
- Restrict
Constructor Parameter Type:
Usingconverters?: Cdirectly links the input parameter to the generic type. TypeScript will automatically inferCasConverterswhen you pass a converter object, or use the defaultundefinedwhen you don't.
How This Works in Practice
No converter provided:
const service = new Service(); const result = await service.read("any-key"); // result is BufferHere,
Cdefaults toundefined, soNames<C>becomesstring | numberandValue<C, N>becomesBuffer.Converter provided:
const tempConverter = { convert: (buf: Buffer) => buf.readUInt16LE(0) } as Converter<number>; const service = new Service({ temp: tempConverter }); const temp = await service.read("temp"); // temp is numberTypeScript infers
Cas the type of your converter object, soNames<C>iskeyof C(in this case"temp") andValue<C, N>is the inferred type from the converter (number).
Why Your Original Syntax Failed
Writing converters?: C extends Converters tries to use a conditional check as a type, which isn't allowed. Parameters need explicit, concrete types (or types derived from generics with valid constraints). The compiler misinterpreted this invalid syntax as a reference to an unexported private type, hence the TS(4063) error.
内容的提问来源于stack exchange,提问作者Lukas

