-
Notifications
You must be signed in to change notification settings - Fork 64
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Ensure default values don't go through validation, fix type narrowing from defaults #176
Conversation
@@ -1,3 +1,13 @@ | |||
// Hacky conditional type to prevent default/devDefault from narrowing type T to a single value. | |||
// Ideally this could be replaced by something that would enforce the default value being a subset | |||
// of T, without affecting the definition of T itself |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Definitely interested in any better approaches here :(
Before this PR, out = cleanEnv(env, { X: str({ default: 'abc' }) })
will cause out.X
to be inferred as type 'abc'
, rather than string
, so this is an incomplete solution to fix that
// default value should be passed through without running through JSON.parse() | ||
expect(cleanEnv({}, { | ||
FOO: json({ default: { x: 999 } }) | ||
})).toEqual({ FOO: { x: 999 } }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added this test for this PR fixing this issue
@kachkaev thanks for the ping, I'd forgotten about this 😅 I believe the fix for the typing issue you found was already merged in #146 , but it hasn't been published yet as I was hoping to get this PR in as well (due to the regression mentioned above). If you could give this branch a test for your use case and report back that'd be really helpful! 🙏 Then I'll do another check and if all goes well, merge and publish |
I just tried this branch in a project with 12
Type narrowing for choices works like a charm: envalid.str({
choices: ["a", "b", "c"] as const,
default: "a"
}) Output is of type The only way I could ‘break’ it was this: envalid.str({
choices: ["a", "b", "c"] as const,
default: "d"
})
envalid.str<"a", "b", "c">({
choices: ["a", "b", "c"],
default: "d"
}) Output is still of type Thanks a lot for this change, I really like it! 🚀 |
Thanks @kachkaev for taking that for a spin! Just published 7.3.0-beta.1 with this change, let me know if you run into any issues with it |
Thanks for releasing
There are subtle differences in import * as envalid from "envalid";
console.log(envalid); 7.2.2: [Module: null prototype] {
EnvError: [Function: EnvError],
EnvMissingError: [Function: EnvMissingError],
__esModule: true,
accessorMiddleware: [Function: accessorMiddleware],
applyDefaultMiddleware: [Function: applyDefaultMiddleware],
bool: [Function: bool],
cleanEnv: [Function: cleanEnv],
customCleanEnv: [Function: customCleanEnv],
default: {
__esModule: true,
cleanEnv: [Getter],
customCleanEnv: [Getter],
testOnly: [Getter],
EnvError: [Getter],
EnvMissingError: [Getter],
strictProxyMiddleware: [Getter],
accessorMiddleware: [Getter],
applyDefaultMiddleware: [Getter],
makeValidator: [Getter],
bool: [Getter],
num: [Getter],
str: [Getter],
email: [Getter],
host: [Getter],
port: [Getter],
url: [Getter],
json: [Getter],
defaultReporter: [Getter]
},
defaultReporter: [Function: defaultReporter],
email: [Function: email],
host: [Function: host],
json: [Function: json],
makeValidator: [Function: makeValidator],
num: [Function: num],
port: [Function: port],
str: [Function: str],
strictProxyMiddleware: [Function: strictProxyMiddleware],
testOnly: [Function: testOnly],
url: [Function: url]
} 7.3.0-beta.1: [Module: null prototype] {
__esModule: true,
default: {
__esModule: true,
cleanEnv: [Getter],
customCleanEnv: [Getter],
testOnly: [Getter],
EnvError: [Getter],
EnvMissingError: [Getter],
strictProxyMiddleware: [Getter],
accessorMiddleware: [Getter],
applyDefaultMiddleware: [Getter],
makeValidator: [Getter],
bool: [Getter],
num: [Getter],
str: [Getter],
email: [Getter],
host: [Getter],
port: [Getter],
url: [Getter],
json: [Getter],
defaultReporter: [Getter]
}
} |
Thanks for that test and the details! 🙏 7.2.2 was built using typescript 4.3 and the beta was with 4.5, so that must be the difference. I haven't followed the ESM changes too closely but it must be related to that. I might not have time to dig in for the next couple days but let me know if you find any clues or tsconfig settings that might prevent this backwards incompatibility |
Seems like we are dealing with a breaking change in TS 4.4: microsoft/TypeScript#45813. I probably managed to fix it locally by setting import * as envalid from "envalid";
console.log(envalid); in project code produces the same output as for "use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
exports.__esModule = true;
__exportStar(require("./envalid"), exports);
__exportStar(require("./errors"), exports);
__exportStar(require("./middleware"), exports);
__exportStar(require("./types"), exports);
__exportStar(require("./validators"), exports);
__exportStar(require("./reporter"), exports);
//# sourceMappingURL=index.js.map There is a planned fix in TS 4.6: microsoft/TypeScript#47070. I installed I don’t know how comfortable you with using |
Amazing, thank you @kachkaev. Just published envalid@7.3.0-beta.2 using TS 4.6 beta. ts-jest warns but seems to work fine. Let me know if that does the trick! |
Great! Thanks for releasing the new beta! It works like a charm in kachkaev/tooling-for-how-old-is-this-house#29 – no breaking changes 🔥 I will keep the PR open for now and will merge it when the new stable version is out. |
Sounds good, my plan is to wait for 4.6 stable, then update and test, then release a new stable envalid assuming everything looks ok |
2 fixes:
choices
). However it appears to have caused a regression where providing a default value would cause the inferred output type to be that literal value rather than a broader type likestring
. The fix here is kinda hacky but should be suitable for most use cases.choices
still causes the output type to be narrowed to a union type of its possible options