我想知道是否可以find()在 const 数组上创建类型安全:
const MY_ARRAY = [
{ id: "a", name: "AAA" },
{ id: "b", name: "BBB" },
{ id: "c", name: "CCC" },
] as const;
type MyArrayId = (typeof MY_ARRAY[number])["id"]
function find<ID extends MyArrayId>(id: ID): MyTypesafeArrayFind<ID> {
return MY_ARRAY.find(entry => entry.id === id);
}
// TODO: here lies the problem :)
type MyTypesafeArrayFind<ID extends MyArrayId> = unknown
const typesafeResult = find("a");
// I'd expect `typeof typesafeResult` to be { id: "a", name: "AAA" }
// Note: I am *not* interested into something like:
// { id: "a"|"b"|"c", name: "AAA"|"BBB"|"CCC" }
Run Code Online (Sandbox Code Playgroud)
看到这个游乐场
您认为这对于 const 数组可行吗?
作为精度,即使在我的示例中这不是类型安全表达的,id我的数组中的字段也将保证是唯一的(我们可以认为匹配搜索条件的第一个条目将匹配)
预先感谢所有参与人员。
这里有易于理解的解决方案:
const MY_ARRAY = [
{ id: "a", name: "AAA" },
{ id: "b", name: "BBB" },
{ id: "c", name: "CCC" },
] as const;
type MyArray = typeof MY_ARRAY
type MyArrayId = MyArray[number]["id"]
function find<ID extends MyArrayId>(id: ID): MyTypesafeArrayFind<ID>
function find<ID extends MyArrayId>(id: ID) {
return MY_ARRAY.find(entry => entry.id === id);
}
type MyTypesafeArrayFind<Id extends MyArrayId> = Extract<MyArray[number], { id: Id }>
// const typesafeResult: {
// readonly id: "a";
// readonly name: "AAA";
// }
const typesafeResult = find("a")
const fn = (str: string) => {
find(str) // error
}
Run Code Online (Sandbox Code Playgroud)
然而它也有其自身的缺点。不允许find在高阶函数内部使用。您只能使用现有的“id”。
如果你想让它超级安全,并且让你的同事生气,你可以考虑这个解决方案:
const MY_ARRAY = [
{ id: "a", name: "AAA" },
{ id: "b", name: "BBB" },
{ id: "c", name: "CCC" },
] as const;
type MyArray = typeof MY_ARRAY
type MyArrayId = MyArray[number]["id"]
/**
* Returns true if provided generic is literal string type
*/
type IsLiteralString<Str extends string> = string extends Str ? false : true
const withTuple = <
Elem extends { id: string },
List extends Elem[]
>(tuple: readonly [...List]) => {
function curry<Id extends List[number]['id']>(id: Id): Extract<List[number], { id: Id }>
function curry<Id extends string>(id: IsLiteralString<Id> extends true ? Id extends List[number]['id'] ? Id : never : Id): List[number] | undefined
function curry(id: string) {
return tuple.find(entry => entry.id === id);
}
return curry
}
const find = withTuple(MY_ARRAY)
// {
// readonly id: "a";
// readonly name: "AAA";
// }
const typesafeResult = find("a")
let id = 'any id'
const typesafeResult2 = find(id,) // Element | undefined, expected and safe
const typesafeResult23 = find('aa',) // expected error, it is useles to use id which is not exists in our list
Run Code Online (Sandbox Code Playgroud)
IsLiteralString- 检查提供的泛型是文字字符串类型还是只是string非推断类型。
withTuple- 期望您的推理列表并返回所需的函数。
curry- 在内部声明的函数withTuple是带有重载的函数。
第一个函数重载function curry<Id extends List[number]['id']>(id: Id): Extract<List[number], { id: Id }>推断数组中id存在并推断返回类型。id
第二个函数重载
function curry<Id extends string>(id: IsLiteralString<Id> extends true ? Id extends List[number]['id'] ? Id : never : Id): List[number] | undefined
string允许您为高阶函数提供非递归常规类型。还提供了id文字,并且它不存在于您的数组中,TS 将禁止您使用它。我称之为“打字稿否定”。您可以在我的文章和此答案中找到有关此方法的更多信息
| 归档时间: |
|
| 查看次数: |
540 次 |
| 最近记录: |