Property 'describe' does not exist on type 'instantiateType<inferTupleExpression<...>, {}>'

const create = <T extends number>(code: T) =>
type(["===", code]).describe("abc") as Type<T>;
const create = <T extends number>(code: T) =>
type(["===", code]).describe("abc") as Type<T>;
This used to work, but now in the latest version it results in error:
Property 'describe' does not exist on type 'instantiateType<inferTupleExpression<readonly ["===", T], {}, bindThis<readonly ["===", T]>>, {}>'
Property 'describe' does not exist on type 'instantiateType<inferTupleExpression<readonly ["===", T], {}, bindThis<readonly ["===", T]>>, {}>'
6 Replies
SynthLuvr
SynthLuvrOP2mo ago
Casting works
const Dummy = type(["===", 200]);
const create = <T extends number>(code: T) =>
(type(["===", code]) as Dummy).describe("abc") as Type<T>;
const Dummy = type(["===", 200]);
const create = <T extends number>(code: T) =>
(type(["===", code]) as Dummy).describe("abc") as Type<T>;
ssalbdivad
ssalbdivad2mo ago
As mentioned previously if you're creating wrapper functions like this, explicit return types is likely your best bet as TS has whacky behavior trying to instantiate generic params within a function.
SynthLuvr
SynthLuvrOP2mo ago
This is annoying, casting doesn't work because it changes the type definition I'm a bit stuck on this, not sure how to call describe without messing up the types I really dislike it but the only way I can figure out how to fix the issue is to do this:
const Dummy = type({});
type Dummy = typeof Dummy;
const codeType = ["===", code] as const;
type codeType = typeof codeType;

return {
error: type({
message,
code: (type(["===", code]) as Dummy).describe(
`Always equal to ${code}`,
) as unknown as codeType,
}),
};
const Dummy = type({});
type Dummy = typeof Dummy;
const codeType = ["===", code] as const;
type codeType = typeof codeType;

return {
error: type({
message,
code: (type(["===", code]) as Dummy).describe(
`Always equal to ${code}`,
) as unknown as codeType,
}),
};
ugh, that creates more problems I think updating from 2.0.0-rc.12 to 2.0.0-rc.13 is the real problem here, so maybe I'll stick with the older version for now
ssalbdivad
ssalbdivad2mo ago
When you're writing generic functions like this, the easiest thing to do is usually have an external signature that incorporates the generic, and internally have a wider definition so you don't have to worry about how TS tries to infer stuff like this. One option would be if you use function instead of an arrow:
function create<T extends number>(code: T): Type<T>
function create(code: number): type.Any {
return type(["===", code]).describe("abc")
}
function create<T extends number>(code: T): Type<T>
function create(code: number): type.Any {
return type(["===", code]).describe("abc")
}
Keep in mind externally you'll always get the narrowed result with this kind of declaration- the type.Any version is just for the internal types.
SynthLuvr
SynthLuvrOP2mo ago
This works great, except in this situation:
function create<T extends number>(code: T): Type<T>
function create(code: number): type.Any {
return type(["===", code]).describe("abc")
}

function inner<T extends number>(code: T) {
const codeType = create(code);
type({codeType})
}
function create<T extends number>(code: T): Type<T>
function create(code: number): type.Any {
return type(["===", code]).describe("abc")
}

function inner<T extends number>(code: T) {
const codeType = create(code);
type({codeType})
}
ssalbdivad
ssalbdivad2mo ago
Yeah this is really annoying you probably need to do the same thing for inner It's a TS issue though like I said there's not really much I can do to help TS understand that T is going to be a number and it should treat it that way when instantiating the generic It's already constrained to number so internally it should be able to evaluate that
Want results from more Discord servers?
Add your server