类型为R和K的Typescript扩展了R的类型,如何声明R[K]是string类型
假设我有接口记录,它看起来像类型为R和K的Typescript扩展了R的类型,如何声明R[K]是string类型,typescript,Typescript,假设我有接口记录,它看起来像 interface Record { id: string; createdBy: string; dateCreated: string; } 和界面结果,如下所示: interface Result<R extends Record> { records: R[]; referredRercords: { [ref:string]: Record; } } 有没有一种方法可以确保typescript类型R[K]确实是一个字符串
interface Record {
id: string;
createdBy: string;
dateCreated: string;
}
和界面结果,如下所示:
interface Result<R extends Record> {
records: R[];
referredRercords: { [ref:string]: Record; }
}
有没有一种方法可以确保typescript类型R[K]确实是一个字符串,而不用事先进行未知的转换?或者这就是处理这个问题的方法
编辑:
这同样有效,我更喜欢它,所以我会选择它,除非有人有更好的:
let key = record[refProp];
let val = (typeof key === 'string') ? result.referredRecords[key] : undefined;
请注意,在命名的
记录中已经有一个类型
,它代表一个对象类型,其键为K
,值为V
。我要将您的类型重命名为MyRecord
,以区分它们。事实上,内置的Record
类型非常有用,我可能会(巧合地)在回答这个问题时使用它
下面是新代码:
interface MyRecord {
id: string;
createdBy: string;
dateCreated: string;
}
interface Result<R extends MyRecord> {
records: R[];
referredRecords: { [ref: string]: MyRecord; }
}
function mergeRecords<R extends MyRecord, K extends keyof R>(
result: Result<R>, refs: [K, string][]
) {
return result.records.map((record) => {
let refsObj = refs.reduce((acc, [refProp, toProp]) => {
let key: string = record[refProp]; // error, not a string
let val = result.referredRecords[key];
return Object.assign(acc, { [toProp]: val });
}, {});
return Object.assign(record, refsObj);
});
}
哎呀
理想情况下,您应该约束
R
(可能还有K
)类型,以便让编译器知道发生了什么。这里有一种方法:
// return the keys from T whose property types match the type V
type KeysMatching<T, V> = {
[K in keyof T]-?: T[K] extends V ? K : never
}[keyof T];
function mergeRecords<
R extends MyRecord & Record<K, string>,
K extends KeysMatching<R, string>
>(
result: Result<R>, refs: [K, string][]
) {
return result.records.map((record) => {
let refsObj = refs.reduce((acc, [refProp, toProp]) => {
let key: string = record[refProp]; // no error now
let val = result.referredRecords[key];
return Object.assign(acc, { [toProp]: val });
}, {});
return Object.assign(record, refsObj);
});
}
现在,第一次调用按需要失败,因为“foo”
处的属性不是字符串,而第二次调用仍然成功,因为“bar”
处的属性没有问题
好的,希望能有帮助。祝你好运 回答得很好。非常感谢,非常彻底,将尽快测试并接受。如果这是可行的,我可能会问另一个关于键入返回值的问题,如果你认为你也能帮上忙的话。我得到了大部分信息,它肯定是可行的,你能再解释一下键匹配类型吗?具体来说,“-?”部分的意思是什么,以及结尾的[keyof T]是什么?
interface MyRecord {
id: string;
createdBy: string;
dateCreated: string;
}
interface Result<R extends MyRecord> {
records: R[];
referredRecords: { [ref: string]: MyRecord; }
}
function mergeRecords<R extends MyRecord, K extends keyof R>(
result: Result<R>, refs: [K, string][]
) {
return result.records.map((record) => {
let refsObj = refs.reduce((acc, [refProp, toProp]) => {
let key: string = record[refProp]; // error, not a string
let val = result.referredRecords[key];
return Object.assign(acc, { [toProp]: val });
}, {});
return Object.assign(record, refsObj);
});
}
interface Oops extends MyRecord {
foo: number;
bar: string;
}
declare const result: Result<Oops>;
mergeRecords(result, [["foo", "bar"]]); // okay, but it shouldn't accept "foo"!
mergeRecords(result, [["bar", "baz"]]); // okay
// return the keys from T whose property types match the type V
type KeysMatching<T, V> = {
[K in keyof T]-?: T[K] extends V ? K : never
}[keyof T];
function mergeRecords<
R extends MyRecord & Record<K, string>,
K extends KeysMatching<R, string>
>(
result: Result<R>, refs: [K, string][]
) {
return result.records.map((record) => {
let refsObj = refs.reduce((acc, [refProp, toProp]) => {
let key: string = record[refProp]; // no error now
let val = result.referredRecords[key];
return Object.assign(acc, { [toProp]: val });
}, {});
return Object.assign(record, refsObj);
});
}
interface Oops extends MyRecord {
foo: number;
bar: string;
}
declare const result: Result<Oops>;
mergeRecords(result, [["foo", "bar"]]); // error on "foo"
mergeRecords(result, [["bar", "baz"]]); // okay