Type checker doesn't understand string literal type in computed property key
whatisaphone opened this issue · 5 comments
TypeScript Version: nightly (2.4.0-dev.20170502)
Code
interface Thing {
readonly foo: number;
}
function bar(t: Thing) { }
function qux(key: 'foo', value: number) {
bar({ foo: 5 }); // works
bar({ [key]: 5 }); // doesn't work
}
Expected behavior:
Both calls typecheck.
Actual behavior:
index.ts(9,7): error TS2345: Argument of type '{ [x: string]: number; }' is not assignable to parameter of type 'Thing'.
Property 'foo' is missing in type '{ [x: string]: number; }'.
Here's a more real-world example where this would be helpful, in the context of React:
import * as React from 'react';
import { ChangeEvent } from 'react';
interface LoginState { username: string; password: string; }
export class Login extends React.Component<{}, LoginState> {
public state: LoginState = { username: '', password: '' };
private onChange(event: ChangeEvent<HTMLInputElement>, property: keyof LoginState) {
this.setState({ [property]: event.target.value }); // this doesn't work!
}
public render() {
return (
<form>
<input value={this.state.username}
onChange={(e) => this.onChange(e, 'username')}/>
<input type="password"
value={this.state.password}
onChange={(e) => this.onChange(e, 'password')}/>
<input type="submit" value="Login"/>
</form>
);
}
}
Relevant definition of setState
:
class Component<P, S> {
setState<K extends keyof S>(state: Pick<S, K>, callback?: () => any): void;
}
Right now the commented line in onChange
does not typecheck. The compiler doesn't realize that a { [k: keyof LoginState]: string }
can be assigned to a Pick<LoginState, K extends keyof LoginState>
.
We currently bypass this by calling this.setState({ ...this.state, [property]: event.target.value })
, but this essentially bypasses the typechecker (e.g. calling this.setState({ ...this.state, foo: 'bar' })
compiles as well). It's also passing more keys than necessary to setState
, and making it do unnecessary work.
I have a similar issue.
type theProp = 'someprop';
const propName: theProp = 'someprop';
interface Some {
someprop?: number;
}
// Correctly gives error
const literalProp: Some {
someprop: true,
}
// Could know that the only possible value of `prop` is `someprop` and give error
const dynamicProp: Some = {
[propName]: true,
}
This bug should be fixed. I am also having this issue. See this post on Stackoverflow: https://stackoverflow.com/questions/46361905/property-is-missing-in-type-x-string-string