typescript - Defining a Record object with a single known key and its value
问题描述
Apologies, this is kind of complicated, and I've been unable to create a simpler reproduction than what I've got here. Feel free to edit the question title with something a bit more specific or descriptive.
I want to define a base data type of the shape:
type AbstractBaseTypes = {
[key: string] : {
inputTypes: Record<string, unknown>;
outputType: unknown;
}
}
That is, there is a list of keys, each have corresponding record of input data, as well as a corresponding output datatype.
As an example, one would then create a 'concrete' version of this data type, like such:
type KnownBaseTypes = {
"text": {
inputTypes: {
label: string;
maxLength: number;
}
outputType: string;
},
"number": {
inputTypes: {
label: string;
min: number;
max: number;
},
outputType: number;
}
}
The ultimate goal I'm going for is I want to create map, that for each of those keys, we create a function that takes three parameters:
- control object = a piece of data describing the key we are talking about, and the input data as described in that KnownBaseTypes.
- data - An 'partially known' record. ie. we know one key of it (from the control object) and its value type (based on the KnownBaseTypes outputType),
- callback - accepts the value of the output type.
ie. Here's how I would do this in plain javascript.
const myDataManipulationMap = {
"text": (data, controlObject, callback) => {
const {
key, inputData
} = controlObject;
const {
label, maxLength
} = inputData
const newString = (label + data[key]).substr(0, maxLength);
const newData = {
...data,
[controlObject.key]: newString
};
callback(newData);
},
"number": (data, controlObject, callback) => {
const {
key, inputData
} = controlObject;
const {
min, max, label
} = inputData
const newNumber = min + Math.random() * (max - min);
const newData = {
...data,
[controlObject.key]: newString
};
callback(newData);
}
}
My approach for doing typings on this goes like this:
My control object:
type ControlObject<T extends AbstractBaseTypes, K extends keyof T> = {
key: string;
inputData: T[K]["inputTypes"];
};
And my partially known data references the key of the control object:
type PartiallyKnownDataObject<T extends AbstractBaseTypes, K extends keyof T > = {
[ key in ControlObject<T, K>["key"] ] : T[K]["outputType"]
}
Putting it all together:
type DataGenerationMap<T extends AbstractBaseTypes> = {
[K in keyof T]: (
data: PartiallyKnownDataObject<T,K>,
controlObject: ControlObject<T,K>,
callback: (data: PartiallyKnownDataObject<T,K>) => void
) => void;
}
This almost does what I want.
The problem is, inside my functions 'partially known data' is asserted to have any number of keys, all with the outputType
data type.
Whereas, I want to be able to only make assumptions about the type of the data accessible by controlObject.key
. Attempts to access other keys should reveal an unknown data type.
const myMap : DataGenerationMap<KnownBaseTypes> = {
"text": (data, controlObject, callback) => {
//snip
},
"number": (data, controlObject, callback) => {
const {
key, inputData
} = controlObject;
const {
min, max
} = inputData
const a : string = min; // expected error
const b: number = data.foo; //<-- should error
callback(); //expected error
console.log(controlObject.inputData.foo); //expected error;
const newNumber = min + Math.random() * (max - min);
const newData = {
...data,
[controlObject.key]: newNumber
};
callback(newData);
}
}
What's going on here?
解决方案
看看这里:
type AbstractBaseTypes = {
[key: string]: {
inputTypes: Record<string, unknown>;
outputType: unknown;
}
}
type KnownBaseTypes = {
"text": {
inputTypes: {
label: string;
maxLength: number;
}
outputType: string;
},
"number": {
inputTypes: {
min: number;
max: number;
},
outputType: number;
}
}
type ControlObject<T extends AbstractBaseTypes, K extends keyof T> = {
key: keyof KnownBaseTypes; // <-- you used `string` as a key type, that's why you ended up with {[index:string]:number} type
inputData: T[K]["inputTypes"];
};
type PartiallyKnownDataObject<T extends AbstractBaseTypes, K extends keyof T> = {
[key in ControlObject<T, K>["key"]]: T[K]["outputType"]
}
type DataGenerationMap<T extends AbstractBaseTypes> = {
[K in keyof T]: (
data: PartiallyKnownDataObject<T, K>,
controlObject: ControlObject<T, K>,
doSomethingWithdData: (data: PartiallyKnownDataObject<T, K>) => void
) => void;
}
type Result = PartiallyKnownDataObject<KnownBaseTypes, "number">
const myMap: DataGenerationMap<KnownBaseTypes> = {
"text": (data, controlObject, callback) => {
const {
key, inputData
} = controlObject;
const {
label, maxLength
} = inputData
const newString = (label + data[key]).substr(0, maxLength);
const newData = {
...data,
[controlObject.key]: newString
};
callback(newData);
},
"number": (data, controlObject, callback) => {
const {
key, inputData
} = controlObject;
const {
min, max
} = inputData
const a: string = min; // expected error
console.log(data.foo); // <-- should error
callback(); //expected error
console.log(controlObject.inputData.foo); //expected error;
const newNumber = min + Math.random() * (max - min);
const newData = {
...data,
[controlObject.key]: newNumber
};
callback(newData);
}
}
它对你有用吗?
推荐阅读
- python - 如何从以下列表中对总和数字进行排序
- angular - 无法获取空的用户表单
- oracle - Oracle 包和在公共变量中使用 select 语句
- google-cloud-platform - 如何在 Cloud Composer 中重启网络服务器
- javascript - 从 JSON 序列化 BigDecimal、BigNumber、BigInt 等
- python - 有没有办法检测脚本本身中的代码块是否包含特定单词
- c - 尝试读取 json 文件时出现分段错误
- c# - 使用 .NET 的 Google 动态链接
- java - 无法打印数字金字塔
- php - PHP登录和注册表错误处理