-
Notifications
You must be signed in to change notification settings - Fork 12.5k
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
Function interface doesn't type check return value for extra keys #12632
Comments
I'm not necessarily sure at which point in the process the object literal loses its freshness. @carloslfu is there any reason you're trying to prohibit excess properties here? |
Suppose this scenario: function fun1 (params: { a: number, b: string }) : { c: number, d: string } {
return {
c: 0,
d: '',
}
}
// this is detected by compiler
function fun2 (params: { a: number, b: string }) : { c: number, d: string } {
return {
c: 0,
d: '',
x: 0
}
}
// I want to be able to do (to be DRY):
interface F {
(params: { a: number, b: string }): { c: number, d: string }
}
const fun1_v2: F = function (params) {
return {
c: 0,
d: '',
}
}
// this function has the same type as fun2, but is not detected by compiler
const fun2_v2: F = function (params) {
return {
c: 0,
d: '',
x: 0
}
} |
As @DanielRosenwasser asked
I'd like give an additional use case for this feature. I've an API that is similar to webpack-block. So the basic idea is that we want to create a configuration object with a lot of fields, and we'd like to use some form of composition to encapsulate similar patterns in that object. To keep it simple let's say that to produce the following configuration object: const config = {
entry: "./main.js",
output: {
path: __dirname + "/build",
filename: "bundle.js"
}
}; we want to write the following code: const config = createConfig([
entryPoint('./main.js'),
setOutput('./build/bundle.js'),
]); and have the type checker ensure that the config is valid: interface IConfig {
entry: string;
output: {
path: string,
filename: string,
};
} This composition design could almost already work as shown in #12769 (comment). We could indeed create a partial config type: type Partial<T> = { [P in keyof T]?: Partial<T[P]> };
type PartialConfig = Partial<IConfig>; and have Now the problem is that we don't want type PartialConfigTransformer = (input: PartialConfig | {}) => PartialConfig; that way utilities functions could execute more complex transformations on the configuration object, and not be limited to inserting additional keys. However now if we introduce a typo again ( The minimal problem is exposed in this snippet: Note that if you had an explicit type annotation for the callback return type, the typechecker is working as expected: but I would expect that this annotation would be inferred from the |
Just ran into this: interface I { x: number; y: number; }
[1,2,3].map<I>(n => ({ x: n, y: n, n })); This is a pretty common pattern in TypeScript's own codebase so we should support getting the contextual type on the object literal and checking for extra properties there. |
@sandersn is this fixed? |
I only changed freshness checking of nested object literals when contextually typed by a union. I think that’s the only recent change, so I don’t think it’s fixed. I’d have to test it to know for sure, though. |
So.... any timeline on when this will be fixed? TypeScript Version: 2.7.2 our use case: enum EConstants {
FOO = 'foo',
BAR = 'bar',
}
interface IState {
id: string,
name: string,
}
function converter<EKeys extends string, T>(
functionMap: {[Key in EKeys] : (objectIn: T) => T}
) {
return functionMap
}
let result = converter<EConstants, IState>({
[EConstants.FOO]: (objectIn) => ({
...objectIn,
extraProp: 'extra' // <-- this isn't detected as invalid!
// even though the intellisense for converter
// can see that this function should return an IState type
}),
[EConstants.BAR]: (objectIn) => ({
...objectIn
})
}) |
Object literals lose their freshness when widened, and we widen return types. I tried a couple of experiments:
function f(name: string, age: number) { return { name, age } }
let justName: { name: string } = f("Bob", 42) Now you get an error that To make this work, we would need to think about all the places where freshness would need to be removed: function declarations would be one, assignments another. I think there are others. |
Going to track at #241 |
TypeScript Version: 2.1.1 / nightly (2.2.0-dev.201xxxxx)
See next code, also in This playground.
Code
Expected behavior:
'g' function should be detected as an error, because return type of interface F doesn't have key 'b', just haves 'a' key
Actual behavior:
'g' function doesn't display any error
The text was updated successfully, but these errors were encountered: