Issue
I'm running into very weird behavior when using identity functions. I'm writing a wizard system with schema (attached is a playground link with a very simplified version), and using constrained identity function to get inference.
The problem occurs in one of the properties that cannot be inferred when I use either of these:
- When the returned value from the identity function is using the
returnkeyword (rather than a single-line return wrapped with parentheses). OR - When declaring an optional argument in the identity function. The argument is declared in the type definition of the identity function, and when using
Parameters<typeof myFunction>it's inferred correctly both when declaring the argument and when I not.
Both these issues are super weird to me, which means I'm either missing something very fundamental or I have found 2 rare bugs.
This reproduces in all available playground versions (tried down to 3.3.3), and also in 4.8.
Playground link with relevant code
Probably better to check the playground for code examples, but there:
TYPES DECLERATIONS:
type Schema = Record<string, unknown> // modified from original for the sake of the example, if it doesn't make sense
type StepFunction<
TSchema extends Schema = Schema,
> = (anything: unknown) => {
readonly schema: TSchema
readonly toAnswers?: (keys: keyof TSchema) => unknown
}
function step<TSchema extends Schema = Schema>(
stepVal: StepFunction<TSchema>,
): StepFunction<TSchema> {
return stepVal
}
EXAMPLES: Notice the returned object of all functions is the same! The differences are in:
- whether we use the
returnkeyword or not (?!?!) - Whether we have the argument for the
stepfunction or not. not that if I doParameters<typeof myStepValue>even when the argument is missing, it's inferred correctly (!)
// WORKS: `keys` is inferred based on the `schema`
// - no argument for `step` function
// - no `return` keyword
const workingExample = step(() => ({
schema: {
attribute: 'anything',
},
toAnswers: keys => {
// RESULT: `keys` inferred successfully as `attribute`
type Test = string extends typeof keys ? never : 'true'
const test: Test = 'true'
return { test }
},
}))
// FAILS: `keys` is not inferred based on the `schema`
// - has argument for `step` function
const nonWorkingA = step(_something => ({
schema: {
attribute: 'anything',
},
toAnswers: keys => {
// RESULT: `keys` failed to inferred hence defaults to `string`
type Test = string extends typeof keys ? never : 'true'
const test: Test = 'true'
return { test }
},
}))
// FAILS: `keys` is not inferred based on the `schema`
// - has `return` keyword rather than a "single-return" return with parentheses
const nonWorkingB = step(() => {
return {
schema: {
attribute: 'anything',
},
toAnswers: keys => {
// RESULT: `keys` failed to inferred hence defaults to `string`
type Test = string extends typeof keys ? never : 'true'
const test: Test = 'true'
return { test }
},
}
})
Solution
Apparently this is a TypeScript inference issue about context-sensitive expressions (see https://github.com/microsoft/TypeScript/issues/49951 and https://github.com/microsoft/TypeScript/issues/47599).
However, I did find a temp solution - by wrapping the return value with an identity function, the edge cases work.
e.g.:
step(_something => (identityOfStep({
schema: {
attribute: 'anything',
},
toAnswers: keys => {
// RESULT: `keys` failed to inferred hence defaults to `string`
type Test = string extends typeof keys ? never : 'true'
const test: Test = 'true'
return { test }
},
})))
Answered By - ShayDavidson
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.