Я хочу сделать что-то вроде этого:
document("post").insert({ ... } /* TYPE SHOULD BE AUTOMATICALLY DETERMINED BY TYPESCRIPT */ );
Функция document()
возвращает объект, содержащий такие функции, как insert
. Объект также содержит свойство document
, которому присваивается значение аргумента, переданного функции document()
. Объект имеет тип DocumentObject
, и такие функции, как insert
, могут обращаться к свойству document
с помощью this.document
. Все работает нормально, но я хочу insert
принять аргумент определенного типа, определяемого значением this.document
.
Как я могу иметь параметры, типы которых различаются в зависимости от значения другой переменной. Это заявление insert
:
export default async function (this: DocumentObject, record: ???): Promise<...> {...}
Переменная this.document
(доступ к которой можно получить из тела функции) может содержать следующие значения: post
, user
, comment
. У меня также есть соответствующие типы для каждого возможного значения: Post
, User
, Comment
. Вот какthis.document
определяется:
document: "post" | "comment" | "user" = ...;
Мой вопрос: как я могу использовать Typescript для сопоставления каждого значения с соответствующим типом, чтобы я мог назначить этот тип параметру record
? Возможно ли это вообще в Typescript?
Примечание. Не является дубликатом Условный тип параметра, основанный на значении другого параметра , и Условный тип, основанный на значении другого ключа; Они предложили мне использовать перегрузки функций, есть ли другой способ сделать это?
(см. предыдущий комментарий) Соответствует ли этот подход вашим потребностям? Если да, то я напишу ответ с объяснением; если нет, то что мне не хватает?
@jcalz О да, это отлично работает, именно то, что я хотел!
Я напишу ответ, когда у меня будет возможность.
Вы можете объявить дополнительный тип, который сопоставляет ключ документа с предполагаемыми типами, и использовать дженерики в своей функции для создания ассоциации с индексированными типами.
type PostDoc = {author: string}
type UserDoc = {name:string}
type CommentDoc = {text:string}
type DocumentObject = {
document:"post"|"user"|"comment"
}
type DocumentTypes = {
post: PostDoc,
user: UserDoc,
comment:CommentDoc
}
async function insert<D extends DocumentObject, R extends DocumentTypes[D["document"]]>(doc:D, record:R) {
...
}
Тогда тип второго параметра зависит от значения поля document
.
insert({document:"post"}, {author:"a"})
insert({document:"user"}, {name:"b"})
insert({document:"comment"}, {text:"c"})
insert({document:"comment"}, {name:"d"}) // TypeError
insert({document:"user"}, {author:"e"}) // TypeError
Похоже, вы неправильно поняли мой код: параметр this
в функции не является параметром, который следует заменять аргументом; вместо этого это то, как набирается ключевое слово this
. Кстати, это называется «Fake This». typescriptlang.org/docs/handbook/release-notes/…. В любом случае, будет ли это работать, если this
не является параметром для передачи? Мол, это сработает: document("user").insert({ name: "a" })
?
Сначала вы, вероятно, захотите написать интерфейс сопоставления, который представляет связь между свойством document
и допустимым типом аргумента insert
:
interface DocumentMap {
post: Post;
user: User;
comment: Comment;
}
Как только мы это сделаем, мы сможем сделать DocumentObject
обобщенным в типе K
свойства document
следующим образом:
interface DocumentObject<K extends keyof DocumentMap> {
document: K;
insert(record: DocumentMap[K]): Promise<any>;
}
Здесь K
ограничено до keyof DocumentMap
, что является всего лишь типом объединения "post" | "user" | "comment"
по желанию. Затем метод insert()
принимает аргумент record
индексированного типа доступа DocumentMap[K]
.
Наконец, ваша функция document
является универсальной по типу K
своего аргумента и возвращает значение типа DocumentObject<K>
:
declare function document<K extends keyof DocumentMap>(k: K): DocumentObject<K>;
Давайте проверим это:
document("post").insert(post); // okay
document("user").insert(user); // okay
document("comment").insert(comment); // okay
document("user").insert(post); // error
Выглядит неплохо. Когда вы вызываете document("user")
, вы получаете DocumentObject<"user">
типа {document: "user", insert(record: User): Promise<any>}
, поэтому метод insert()
принимает User
и отклоняет Post
.
Пожалуйста, отредактируйте код здесь, чтобы он стал минимально воспроизводимым примером, где мы можем скопировать и вставить его в наши собственные IDE и немедленно приступить к работе над ним. В настоящее время нам нужно написать свои собственные типы
Post
,User
,Comment
и т. д. И каким бы ни был тип выводаPromise<>
.