refactor/better naming for inflection search types

This commit is contained in:
adueck 2022-10-04 14:45:37 +05:00
parent ced17170a7
commit 0ad08abb88
7 changed files with 71 additions and 72 deletions

View File

@ -134,7 +134,7 @@ class App extends Component<RouteComponentProps, State> {
wordlist: [], wordlist: [],
reviewTasks: [], reviewTasks: [],
user: readUser(), user: readUser(),
powerResults: undefined, inflectionSearchResults: undefined,
}; };
this.handleOptionsUpdate = this.handleOptionsUpdate.bind(this); this.handleOptionsUpdate = this.handleOptionsUpdate.bind(this);
this.handleTextOptionsUpdate = this.handleTextOptionsUpdate.bind(this); this.handleTextOptionsUpdate = this.handleTextOptionsUpdate.bind(this);
@ -146,7 +146,7 @@ class App extends Component<RouteComponentProps, State> {
this.handleRefreshWordlist = this.handleRefreshWordlist.bind(this); this.handleRefreshWordlist = this.handleRefreshWordlist.bind(this);
this.handleRefreshReviewTasks = this.handleRefreshReviewTasks.bind(this); this.handleRefreshReviewTasks = this.handleRefreshReviewTasks.bind(this);
this.handleDictionaryUpdate = this.handleDictionaryUpdate.bind(this); this.handleDictionaryUpdate = this.handleDictionaryUpdate.bind(this);
this.handlePowerSearch = this.handlePowerSearch.bind(this); this.handleInflectionSearch = this.handleInflectionSearch.bind(this);
} }
public componentDidMount() { public componentDidMount() {
@ -245,7 +245,7 @@ class App extends Component<RouteComponentProps, State> {
e.preventDefault(); e.preventDefault();
if (e.repeat) return; if (e.repeat) return;
if (!this.state.searchValue) return; if (!this.state.searchValue) return;
this.handlePowerSearch(); this.handleInflectionSearch();
}); });
Mousetrap.bind(["ctrl+s", "command+s"], (e) => { Mousetrap.bind(["ctrl+s", "command+s"], (e) => {
if (this.state.user?.level === "basic") return; if (this.state.user?.level === "basic") return;
@ -406,7 +406,7 @@ class App extends Component<RouteComponentProps, State> {
searchValue: "", searchValue: "",
results: [], results: [],
page: 1, page: 1,
powerResults: undefined, inflectionSearchResults: undefined,
}); });
if (this.props.location.pathname !== "/") { if (this.props.location.pathname !== "/") {
this.props.history.replace("/"); this.props.history.replace("/");
@ -417,7 +417,7 @@ class App extends Component<RouteComponentProps, State> {
searchValue, searchValue,
results: dictionary.search({ ...prevState, searchValue }), results: dictionary.search({ ...prevState, searchValue }),
page: 1, page: 1,
powerResults: undefined, inflectionSearchResults: undefined,
})); }));
if (this.props.history.location.pathname !== "/search") { if (this.props.history.location.pathname !== "/search") {
this.props.history.push("/search"); this.props.history.push("/search");
@ -459,19 +459,19 @@ class App extends Component<RouteComponentProps, State> {
} }
} }
private handlePowerSearch() { private handleInflectionSearch() {
function prepValueForSearch(searchValue: string, textOptions: T.TextOptions): string { function prepValueForSearch(searchValue: string, textOptions: T.TextOptions): string {
const s = revertSpelling(searchValue, textOptions.spelling); const s = revertSpelling(searchValue, textOptions.spelling);
return standardizePashto(s.trim()); return standardizePashto(s.trim());
} }
this.setState({ powerResults: "searching" }); this.setState({ inflectionSearchResults: "searching" });
// need timeout to make sure the "searching" notice gets rendered before things lock up for the big search // need timeout to make sure the "searching" notice gets rendered before things lock up for the big search
setTimeout(() => { setTimeout(() => {
const powerResults = searchAllInflections( const inflectionSearchResults = searchAllInflections(
allEntries(), allEntries(),
prepValueForSearch(this.state.searchValue, this.state.options.textOptionsRecord.textOptions), prepValueForSearch(this.state.searchValue, this.state.options.textOptionsRecord.textOptions),
); );
this.setState({ powerResults }); this.setState({ inflectionSearchResults });
}, 20); }, 20);
} }
@ -563,7 +563,7 @@ class App extends Component<RouteComponentProps, State> {
<Results <Results
state={this.state} state={this.state}
isolateEntry={this.handleIsolateEntry} isolateEntry={this.handleIsolateEntry}
handlePowerSearch={this.handlePowerSearch} handleInflectionSearch={this.handleInflectionSearch}
/> />
</Route> </Route>
<Route path="/new-entries"> <Route path="/new-entries">
@ -572,7 +572,7 @@ class App extends Component<RouteComponentProps, State> {
<Results <Results
state={this.state} state={this.state}
isolateEntry={this.handleIsolateEntry} isolateEntry={this.handleIsolateEntry}
handlePowerSearch={this.handlePowerSearch} handleInflectionSearch={this.handleInflectionSearch}
/> />
: :
<div>No new words added this month 😓</div> <div>No new words added this month 😓</div>

View File

@ -15,19 +15,19 @@ import {
displayPositionResult, displayPositionResult,
} from "../lib/inflection-search-helpers"; } from "../lib/inflection-search-helpers";
import { import {
InflectionSearchResult,
InflectionName, InflectionName,
InflectionFormMatch,
} from "../types/dictionary-types"; } from "../types/dictionary-types";
function InflectionSearchResultDisplay( function InflectionFormMatchDisplay(
{ result, textOptions, entry }: { form, textOptions, entry }:
{ result: InflectionSearchResult, textOptions: T.TextOptions, entry: T.DictionaryEntry } { form: InflectionFormMatch, textOptions: T.TextOptions, entry: T.DictionaryEntry }
) { ) {
function getTransitivity(): "transitive" | "intransitive" | "grammatically transitive" { function getTransitivity(): "transitive" | "intransitive" | "grammatically transitive" {
if (result.form.includes("grammaticallyTransitive")) { if (form.path.includes("grammaticallyTransitive")) {
return "grammatically transitive"; return "grammatically transitive";
} }
if (result.form.includes("transitive")) { if (form.path.includes("transitive")) {
return "transitive"; return "transitive";
} }
if (entry.c?.includes("intrans.")) { if (entry.c?.includes("intrans.")) {
@ -36,15 +36,15 @@ function InflectionSearchResultDisplay(
return "transitive"; return "transitive";
} }
const transitivity = getTransitivity(); const transitivity = getTransitivity();
const isPast = (result.form.includes("past") || result.form.includes("perfect")); const isPast = (form.path.includes("past") || form.path.includes("perfect"));
const isErgative = (transitivity !== "intransitive") && isPast; const isErgative = (transitivity !== "intransitive") && isPast;
const isVerbPos = (x: InflectionName[] | T.Person[] | null) => { const isVerbPos = (x: InflectionName[] | T.Person[] | null) => {
if (x === null) return false; if (x === null) return false;
return (typeof x[0] !== "string"); return (typeof x[0] !== "string");
}; };
return <div className="mb-4"> return <div className="mb-4">
<div className="mb-2"><strong>{displayFormResult(result.form)}</strong></div> <div className="mb-2"><strong>{displayFormResult(form.path)}</strong></div>
{result.matches.map((match, i) => <div className="ml-2" key={i}> {form.matches.map((match, i) => <div className="ml-2" key={i}>
<InlinePs opts={textOptions}>{match.ps}</InlinePs> <InlinePs opts={textOptions}>{match.ps}</InlinePs>
<div className="ml-3 my-2"> <div className="ml-3 my-2">
<em> <em>
@ -57,4 +57,4 @@ function InflectionSearchResultDisplay(
</div>; </div>;
} }
export default InflectionSearchResultDisplay; export default InflectionFormMatchDisplay;

View File

@ -11,7 +11,7 @@ import {
} from "@lingdocs/pashto-inflector"; } from "@lingdocs/pashto-inflector";
import { isPashtoScript } from "./is-pashto"; import { isPashtoScript } from "./is-pashto";
import { import {
InflectionSearchResult, PowerResult, InflectionSearchResult, InflectionFormMatch,
} from "../types/dictionary-types"; } from "../types/dictionary-types";
import { makeAWeeBitFuzzy } from "./wee-bit-fuzzy"; import { makeAWeeBitFuzzy } from "./wee-bit-fuzzy";
// @ts-ignore // @ts-ignore
@ -26,19 +26,19 @@ const relevancySorter = new relevancy.Sorter();
// That's so much better I'm removing the option of skipping compounds // That's so much better I'm removing the option of skipping compounds
// ~4th iteration:~ ignore perfective or imperfective if wasn't present in verb info (not worth it - scrapped) // ~4th iteration:~ ignore perfective or imperfective if wasn't present in verb info (not worth it - scrapped)
export function searchAllInflections(allDocs: T.DictionaryEntry[], searchValue: string): { entry: T.DictionaryEntry, results: InflectionSearchResult[] }[] { export function searchAllInflections(allDocs: T.DictionaryEntry[], searchValue: string): InflectionSearchResult[] {
const index = isPashtoScript(searchValue) ? "p" : "f" const index = isPashtoScript(searchValue) ? "p" : "f"
function sortResultsByRelevancy(arr: PowerResult[]): PowerResult[] { function sortResultsByRelevancy(arr: InflectionSearchResult[]): InflectionSearchResult[] {
return relevancySorter.sort(arr, searchValue, (obj: PowerResult, calc: any) => ( return relevancySorter.sort(arr, searchValue, (obj: InflectionSearchResult, calc: any) => (
calc(removeAccents(obj.results[0].matches[0].ps[index])) calc(removeAccents(obj.forms[0].matches[0].ps[index]))
)); ));
} }
// TODO: could be better to remove the accents on the searchValue as well beforehand // TODO: could be better to remove the accents on the searchValue as well beforehand
function sortMatchesByRelevancy(r: PowerResult): PowerResult { function sortMatchesByRelevancy(r: InflectionSearchResult): InflectionSearchResult {
// first sort all the matches of each form by relevance // first sort all the matches of each form by relevance
const rStage2 = { const rStage2 = {
...r, ...r,
results: r.results.map(x => ({ forms: r.forms.map(x => ({
...x, ...x,
matches: relevancySorter.sort(x.matches, searchValue, (obj: { matches: relevancySorter.sort(x.matches, searchValue, (obj: {
ps: T.PsString; ps: T.PsString;
@ -48,12 +48,12 @@ export function searchAllInflections(allDocs: T.DictionaryEntry[], searchValue:
})) }))
}; };
// then sort the forms by relevance // then sort the forms by relevance
const results = relevancySorter.sort(rStage2.results, searchValue, (obj: InflectionSearchResult, calc: any) => ( const forms = relevancySorter.sort(rStage2.forms, searchValue, (obj: InflectionFormMatch, calc: any) => (
calc(removeAccents(obj.matches[0].ps[index])) calc(removeAccents(obj.matches[0].ps[index]))
)); ));
return { return {
...r, ...r,
results, forms,
}; };
} }
@ -72,7 +72,7 @@ export function searchAllInflections(allDocs: T.DictionaryEntry[], searchValue:
// also do version without directional pronoun on front // also do version without directional pronoun on front
const searchFun = (ps: T.PsString) => !!ps[script].match(searchRegex) const searchFun = (ps: T.PsString) => !!ps[script].match(searchRegex)
// console.time(timerLabel); // console.time(timerLabel);
const results = allDocs.reduce((all: PowerResult[], entry) => { const results = allDocs.reduce((all: InflectionSearchResult[], entry) => {
const type = isNounAdjOrVerb(entry); const type = isNounAdjOrVerb(entry);
if (entry.c && type === "verb") { if (entry.c && type === "verb") {
try { try {
@ -84,12 +84,12 @@ export function searchAllInflections(allDocs: T.DictionaryEntry[], searchValue:
entry, entry,
complement, complement,
); );
const results = searchPile( const forms = searchPile(
conjugation as any, conjugation as any,
searchFun, searchFun,
); );
if (results.length) { if (forms.length) {
return [...all, { entry, results }]; return [...all, { entry, forms }];
} }
return all; return all;
} catch (e) { } catch (e) {
@ -101,9 +101,9 @@ export function searchAllInflections(allDocs: T.DictionaryEntry[], searchValue:
if (entry.c && type === "nounAdj") { if (entry.c && type === "nounAdj") {
const inflections = inflectWord(entry); const inflections = inflectWord(entry);
if (!inflections) return all; if (!inflections) return all;
const results = searchPile(inflections as any, searchFun); const forms = searchPile(inflections as any, searchFun);
if (results.length) { if (forms.length) {
return [...all, { entry, results }]; return [...all, { entry, forms }];
} }
} }
return all; return all;

View File

@ -17,7 +17,7 @@ import { personFromVerbBlockPos } from "@lingdocs/pashto-inflector";
import { import {
InflectionName, InflectionName,
PluralInflectionName, PluralInflectionName,
InflectionSearchResult, InflectionFormMatch,
} from "../types/dictionary-types"; } from "../types/dictionary-types";
const inflectionNames: { inflections: InflectionName[], plural: PluralInflectionName[] } = { const inflectionNames: { inflections: InflectionName[], plural: PluralInflectionName[] } = {
@ -42,17 +42,17 @@ function isPsString(x: T.PsString | ObPile): x is T.PsString {
); );
} }
function isBlockResult(x: InflectionSearchResult[] | BlockResult): x is BlockResult { function isBlockResult(x: InflectionFormMatch[] | BlockResult): x is BlockResult {
return "ps" in x[0]; return "ps" in x[0];
} }
// NOTE: perfectiveSplit needs to be ignored because the [PsString, PsString] structure breaks the search! // NOTE: perfectiveSplit needs to be ignored because the [PsString, PsString] structure breaks the search!
const defaultFieldsToIgnore = ["info", "type", "perfectiveSplit"]; const defaultFieldsToIgnore = ["info", "type", "perfectiveSplit"];
export function searchPile(pile: ObPile, searchFun: (s: T.PsString) => boolean, toIgnore: string[] = []): InflectionSearchResult[] { export function searchPile(pile: ObPile, searchFun: (s: T.PsString) => boolean, toIgnore: string[] = []): InflectionFormMatch[] {
const fieldsToIgnore = [...defaultFieldsToIgnore, toIgnore]; const fieldsToIgnore = [...defaultFieldsToIgnore, toIgnore];
function searchObRecord(record: ObRec): null | BlockResult | SinglePsResult | InflectionSearchResult[] { function searchObRecord(record: ObRec): null | BlockResult | SinglePsResult | InflectionFormMatch[] {
// hit a bottom part a tree, see if what we're looking for is there // hit a bottom part a tree, see if what we're looking for is there
if (Array.isArray(record)) { if (Array.isArray(record)) {
// @ts-ignore // @ts-ignore
@ -68,7 +68,7 @@ export function searchPile(pile: ObPile, searchFun: (s: T.PsString) => boolean,
return searchPile(record, searchFun); return searchPile(record, searchFun);
} }
return Object.entries(pile).reduce((res: InflectionSearchResult[], entry): InflectionSearchResult[] => { return Object.entries(pile).reduce((res: InflectionFormMatch[], entry): InflectionFormMatch[] => {
const [name, value] = entry; const [name, value] = entry;
if (fieldsToIgnore.includes(name)) { if (fieldsToIgnore.includes(name)) {
return res; return res;
@ -83,7 +83,7 @@ export function searchPile(pile: ObPile, searchFun: (s: T.PsString) => boolean,
return [ return [
...res, ...res,
{ {
form: [name], path: [name],
matches: [{ ps: result, pos: null }], matches: [{ ps: result, pos: null }],
}, },
]; ];
@ -96,18 +96,18 @@ export function searchPile(pile: ObPile, searchFun: (s: T.PsString) => boolean,
return [ return [
...res, ...res,
{ {
form: [name], path: [name],
matches: result, matches: result,
} }
]; ];
} }
// Result: Have to keep looking down recursively // Result: Have to keep looking down recursively
// add in the current path to all the results // add in the current path to all the results
const rb: InflectionSearchResult[] = [ const rb: InflectionFormMatch[] = [
...res, ...res,
...result.map((r) => ({ ...result.map((r) => ({
...r, ...r,
form: [name, ...r.form] path: [name, ...r.path]
})), })),
] ]
return rb; return rb;

View File

@ -257,7 +257,7 @@ function IsolatedEntry({ state, dictionary, isolateEntry }: {
<Results <Results
state={{ ...state, results: relatedEntries }} state={{ ...state, results: relatedEntries }}
isolateEntry={isolateEntry} isolateEntry={isolateEntry}
handlePowerSearch={() => null} handleInflectionSearch={() => null}
/> />
</> : <div style={{ height: "500px" }} />} </> : <div style={{ height: "500px" }} />}
<Modal <Modal

View File

@ -15,20 +15,19 @@ import {
import { isPashtoScript } from "../lib/is-pashto"; import { isPashtoScript } from "../lib/is-pashto";
import Entry from "../components/Entry"; import Entry from "../components/Entry";
import { Helmet } from "react-helmet"; import { Helmet } from "react-helmet";
import InflectionSearchResultDisplay from "../components/InflectionSearchResultDisplay"; import InflectionFormMatchDisplay from "../components/InflectionFormMatchDisplay";
import { getTextOptions } from "../lib/get-text-options"; import { getTextOptions } from "../lib/get-text-options";
import { import {
State, State,
InflectionSearchResult,
} from "../types/dictionary-types"; } from "../types/dictionary-types";
export const inflectionSearchIcon = "fas fa-search-plus"; export const inflectionSearchIcon = "fas fa-search-plus";
// TODO: put power results in a prop so we can do it from outside with the keyboard shortcut // TODO: put power results in a prop so we can do it from outside with the keyboard shortcut
function Results({ state, isolateEntry, handlePowerSearch }: { function Results({ state, isolateEntry, handleInflectionSearch }: {
state: State, state: State,
isolateEntry: (ts: number) => void, isolateEntry: (ts: number) => void,
handlePowerSearch: () => void, handleInflectionSearch: () => void,
}) { }) {
const [suggestionState, setSuggestionState] = useState<"none" | "editing" | "received">("none"); const [suggestionState, setSuggestionState] = useState<"none" | "editing" | "received">("none");
const [comment, setComment] = useState<string>(""); const [comment, setComment] = useState<string>("");
@ -67,34 +66,34 @@ function Results({ state, isolateEntry, handlePowerSearch }: {
addSubmission(newEntry, state.user); addSubmission(newEntry, state.user);
setSuggestionState("received"); setSuggestionState("received");
} }
const powerResults = state.powerResults; const inflectionResults = state.inflectionSearchResults;
return <div className="width-limiter"> return <div className="width-limiter">
<Helmet> <Helmet>
<title>LingDocs Pashto Dictionary</title> <title>LingDocs Pashto Dictionary</title>
</Helmet> </Helmet>
{(state.user && (window.location.pathname !== "/word") && suggestionState === "none" && powerResults === undefined) && <button {(state.user && (window.location.pathname !== "/word") && suggestionState === "none" && inflectionResults === undefined) && <button
type="button" type="button"
className={`btn btn-outline-secondary bg-white entry-suggestion-button${state.options.searchBarPosition === "bottom" ? " entry-suggestion-button-with-bottom-searchbar" : ""}`} className={`btn btn-outline-secondary bg-white entry-suggestion-button${state.options.searchBarPosition === "bottom" ? " entry-suggestion-button-with-bottom-searchbar" : ""}`}
onClick={startSuggestion} onClick={startSuggestion}
> >
<i className="fas fa-plus" style={{ padding: "3px" }} /> <i className="fas fa-plus" style={{ padding: "3px" }} />
</button>} </button>}
{(powerResults === undefined && suggestionState === "none" && window.location.pathname === "/search") && <button {(inflectionResults === undefined && suggestionState === "none" && window.location.pathname === "/search") && <button
type="button" type="button"
className={`btn btn-outline-secondary bg-white conjugation-search-button${state.options.searchBarPosition === "bottom" ? " conjugation-search-button-with-bottom-searchbar" : ""}`} className={`btn btn-outline-secondary bg-white conjugation-search-button${state.options.searchBarPosition === "bottom" ? " conjugation-search-button-with-bottom-searchbar" : ""}`}
onClick={handlePowerSearch} onClick={handleInflectionSearch}
> >
<i className={inflectionSearchIcon} style={{ padding: "3px" }} /> <i className={inflectionSearchIcon} style={{ padding: "3px" }} />
</button>} </button>}
{powerResults === "searching" && <div> {inflectionResults === "searching" && <div>
<p className="lead mt-1">Searching conjugations/inflections... <i className="fas fa-hourglass-half" /></p> <p className="lead mt-1">Searching conjugations/inflections... <i className="fas fa-hourglass-half" /></p>
</div>} </div>}
{Array.isArray(powerResults) && <div> {Array.isArray(inflectionResults) && <div>
<h4 className="mt-1 mb-3">Conjugation/Inflection Results</h4> <h4 className="mt-1 mb-3">Conjugation/Inflection Results</h4>
{powerResults.length === 0 && <div className="mt-4"> {inflectionResults.length === 0 && <div className="mt-4">
<div>No conjugation/inflection matches found for <strong>{state.searchValue}</strong></div> <div>No conjugation/inflection matches found for <strong>{state.searchValue}</strong></div>
</div>} </div>}
{powerResults.map((p) => ( {inflectionResults.map((p) => (
<div key={p.entry.ts}> <div key={p.entry.ts}>
<Entry <Entry
key={p.entry.i} key={p.entry.i}
@ -103,11 +102,11 @@ function Results({ state, isolateEntry, handlePowerSearch }: {
isolateEntry={isolateEntry} isolateEntry={isolateEntry}
/> />
<div className="mb-3 ml-2"> <div className="mb-3 ml-2">
{p.results.map((result: InflectionSearchResult, i) => ( {p.forms.map((form, i) => (
<InflectionSearchResultDisplay <InflectionFormMatchDisplay
key={"inf-result" + i} key={"inf-form" + i}
textOptions={textOptions} textOptions={textOptions}
result={result} form={form}
entry={p.entry} entry={p.entry}
/> />
))} ))}
@ -115,7 +114,7 @@ function Results({ state, isolateEntry, handlePowerSearch }: {
</div> </div>
))} ))}
</div>} </div>}
{powerResults === undefined && suggestionState === "none" && state.results.map((entry) => ( {inflectionResults === undefined && suggestionState === "none" && state.results.map((entry) => (
<Entry <Entry
key={entry.i} key={entry.i}
entry={entry} entry={entry}
@ -199,7 +198,7 @@ function Results({ state, isolateEntry, handlePowerSearch }: {
Thanks for the help! Thanks for the help!
</div> </div>
} }
{(((powerResults === undefined) && suggestionState === "none" && state.searchValue && (!state.results.length))) && <div> {(((inflectionResults === undefined) && suggestionState === "none" && state.searchValue && (!state.results.length))) && <div>
<h5 className="mt-2">No Results Found in {state.options.language}</h5> <h5 className="mt-2">No Results Found in {state.options.language}</h5>
{state.options.language === "Pashto" && isPashtoScript(state.searchValue) && <p className="mt-3"> {state.options.language === "Pashto" && isPashtoScript(state.searchValue) && <p className="mt-3">
Click on the <i className={inflectionSearchIcon} /> to search inflections and conjugations Click on the <i className={inflectionSearchIcon} /> to search inflections and conjugations

View File

@ -11,7 +11,7 @@ export type State = {
reviewTasks: import("./functions-types").ReviewTask[], reviewTasks: import("./functions-types").ReviewTask[],
dictionaryInfo: import("@lingdocs/pashto-inflector").Types.DictionaryInfo | undefined, dictionaryInfo: import("@lingdocs/pashto-inflector").Types.DictionaryInfo | undefined,
user: undefined | import("./account-types").LingdocsUser, user: undefined | import("./account-types").LingdocsUser,
powerResults: undefined | "searching" | PowerResult[], inflectionSearchResults: undefined | "searching" | InflectionSearchResult[],
} }
export type DictionaryAPI = { export type DictionaryAPI = {
@ -160,13 +160,13 @@ export type PluralInflectionName = "plural" | "2nd";
// for each form // for each form
// the possible matches, and their person/inflection number // the possible matches, and their person/inflection number
export type PowerResult = {
entry: import("@lingdocs/pashto-inflector").Types.DictionaryEntry,
results: InflectionSearchResult[],
};
export type InflectionSearchResult = { export type InflectionSearchResult = {
form: string[], entry: import("@lingdocs/pashto-inflector").Types.DictionaryEntry,
forms: InflectionFormMatch[],
}
export type InflectionFormMatch = {
path: string[],
matches: { matches: {
ps: import("@lingdocs/pashto-inflector").Types.PsString, ps: import("@lingdocs/pashto-inflector").Types.PsString,
pos: InflectionName[] | import("@lingdocs/pashto-inflector").Types.Person[] | null, pos: InflectionName[] | import("@lingdocs/pashto-inflector").Types.Person[] | null,