Hi
I am trying to create an interface that will allow only specific strings as keys by using index signature but unable to do so. I am hoping it can be added in the next version.
Here is an example:
type RestrictedStyleAttribute = "color" | "background-color" | "font-weight";
interface IRestrictedStyle {
[index: RestrictedStyleAttribute] : string
}
When I do what is above, I get An index signature parameter type must be 'string' or 'number'.
I don't want to use the type string for the index since I don't want any string to be a key in an object that implements IRestrictedStyle interface.
Since the type RestrictedStyleAttribute is a sub type of string, I think, it should be allowed and necessary checks should be made on compile.
Version: 2.3.0-dev.20170412
It seems type system does not work on index signature.
The following code will cause the same error:
type Key = string;
interface Dictionary<T> {
[key: Key]: T;
}
type Index = number;
interface List<T> {
[index: Index]: T;
length: number;
}
Edit:
I think this may be what @nozer want:
type RestrictedStyleAttribute = "color" | "background-color" | "font-weight";
type IRestrictedStyle = {
[T in RestrictedStyleAttribute]: string;
}
Yes, it seems to expect either string or number explicitly without doing any inference about the index type.
That works to some extent. If I don't use optional for the key, it requires all the RestrictedStyleAttribute types to exist in the implementing object. If I use optional, then it accepts empty object as valid too. I do not want empty object for my case.
type IRestrictedStyle = {
[T in RestrictedStyleAttribute]?: string; // makes empty object valid for this type
}
type IRestrictedStyle = {
[T in RestrictedStyleAttribute]: string; // requires keys for all RestrictedStyleAttribute
}
I think you can do string | void
let x:IRestrictedStyle = {}; is still valid when I do string | void. Basically, I would like implementing object to have at least one of the keys.
@nozer
type A = { a: string };
type B = { b: string };
type C = { c: string };
type Result = (A | B | C) & Partial<A & B & C>;
// this will cause error
const result1: Result = {};
// others are OK
const result2: Result = {
a: 'a',
};
const result3: Result = {
b: 'b',
};
const result4: Result = {
a: 'a',
b: 'b',
c: 'c',
};
You'd likely need union types to do that (untested):
type IRestrictedStyle = {
"color": string,
[T in RestrictedStyleAttribute]: string | undefined,
} | {
"background-color": string,
[T in RestrictedStyleAttribute]: string | undefined,
} | {
"font-weight": string,
[T in RestrictedStyleAttribute]: string | undefined,
}
etc. I don't think there's a way to say that the object as at least one of those keys w/o saying it explicitly.
@aaronjensen @ikatyang
That is a bit too long. I could do this for example:
interface ColorAttr {
color: string
}
interface BgColorAttr {
'background-color': string
}
interface FontWeightAttr {
'font-weight': string
}
type IRestrictedStyle = ColorAttr | BgColorAttr | FontWeightAttr;
I am not saying there is not any way to do it. I am merely suggesting a simpler way to express it in the next version.
@ikatyang 's last answer is pretty succinct though; still, a way to do it in the index signature format would be much simpler.
@nozer
interface ColorAttr {
color: string
}
interface BgColorAttr {
'background-color': string
}
interface FontWeightAttr {
'font-weight': string
}
type IRestrictedStyle = ColorAttr | BgColorAttr | FontWeightAttr;
const style: IRestrictedStyle = {
color: 'color',
'background-color': 123, // this wont be an error, since it just has to be one of those
};
@ikatyang Waow, that is interesting. Thanks for pointing that out.
Most helpful comment
Version: 2.3.0-dev.20170412
It seems type system does not work on index signature.
The following code will cause the same error:
Edit:
I think this may be what @nozer want: