Машинопись: Как заполнить свойства объекта их полным именем

Кто-то отправил ответ несколько минут назад, и это было правильно, и оно сработало, но по какой-то причине оно было удалено, поэтому я переношу его для всех, кто, возможно, пропустил его.

Вот он:

for(var i in result) {
                if (i < 5) continue;

                console.log(result[i].id);

                        if (alerted != result[i].id) {
                            $('#chatAudio')[0].play();

                            iziToast.show({
                                message: result[i].notification_text,
                                messageColor: '#424242',
                                backgroundColor: '#fff',
                                theme: 'light',
                                animateInside: true,
                                layout: 1,
                                close: false,
                                position: 'bottomLeft',
                                timeout: 5000,
                                progressBar: false
                            });

                            localStorage.setItem('alerted', result[i].id);
                        }
                }
1
задан MarekKnows.com 18 January 2019 в 21:12
поделиться

1 ответ

Это невозможно со стандартным компилятором TS. Типы TypeScript полностью удаляются во время выполнения, поэтому эта информация больше недоступна.

Однако, если вы скомпилируете свой источник с помощью специального скрипта, а не просто с помощью tsc, это станет возможным.

Чтобы упростить код, я использовал здесь ts-simple-ast .

Это хорошая идея? Нет ... но это было весело.

import { Project, VariableDeclaration, Type, WriterFunction } from 'ts-simple-ast' // ^21.0.0

const project = new Project()
project.addExistingSourceFiles('src/**/*.ts')

// Custom functionality
// Look for variables that have a doc comment with `@autoInit`
// Get their type, and assign values to https://stackoverflow.com/q/54260406/7186598
for (const sourceFile of project.getSourceFiles()) {
    // TODO: Class properties, object literal properties, etc.?
    const declarations = sourceFile.getVariableDeclarations()

    for (const declaration of declarations.filter(hasAutoInitTag)) {
        if (declaration.hasInitializer()) {
            console.warn(`'${declaration.getName()}' has an initializer and @autoInit tag. Skipping.`)
            continue
        }

        const type = declaration.getType()
        const writer = createWriterForType(declaration.getName(), type);

        const parentStatement = declaration.getParent().getParent()
        const index = sourceFile.getStatements().findIndex(statement => statement === parentStatement)

        // Insert after the variable declaration
        sourceFile.insertStatements(index + 1, writer);
    }

    console.log(sourceFile.getFullText())
    // Uncomment once you have verified it does what you want.
    // sourceFile.saveSync()
}

// There's almost certainly a better way to do this.
function hasAutoInitTag(declaration: VariableDeclaration) {
    // Comments are attached to a VariableDeclarationList which contains VariableDeclarations, so
    // get the parent.
    const comments = declaration.getParent().getLeadingCommentRanges().map(range => range.getText())
    return comments.some(comment => comment.includes('@autoInit'))
}

function createWriterForType(name: string, type: Type): WriterFunction {
    return writer => {
        function writeTypeInitializer(nameStack: string[], type: Type) {
            if (type.isString()) {
                // Some logic for non-standard names is probably a good idea here.
                // this won't handle names like '()\'"'
                writer.writeLine(`${nameStack.join('.')} = '${nameStack.slice(1).join('_')}'`)
            } else if (type.isObject()) {
                writer.writeLine(`${nameStack.join('.')} = {}`)
                for (const prop of type.getProperties()) {
                    const node = prop.getValueDeclarationOrThrow()
                    writeTypeInitializer(nameStack.concat(prop.getName()), prop.getTypeAtLocation(node))
                }
            } else {
                console.warn('Unknown type', nameStack, type.getText())
            }
        }

        writeTypeInitializer([name], type)
    }
}

Теперь, менее захватывающее решение.

Вместо описания вашего объекта с помощью интерфейса, вы можете сгенерировать интерфейс из объекта. Затем ключи будут доступны для доступа с помощью функции autoInit, которая может генерировать нужные вам строки. Демонстрация игровой площадки

// Exactly the same as the original interface
type MyObjects = typeof myObjects

let myObjects = {
    Troll: { 
        strength: '',
        dexterity: '',
        wisdom: ''
    },
    Child: {
        health: '',
        wellness: ''
    },
    Kitten: {
        sneakFactor: '',
        color: ''
    }
};

autoInit(myObjects)
console.log(myObjects)

type AutoInitable = { [k: string]: AutoInitable } | string
function autoInit(obj: { [k: string]: AutoInitable }, nameStack: string[] = []) {
    for (const key of Object.keys(obj)) {
        const val = obj[key]
        if (typeof val === 'string') {
            obj[key] = [...nameStack, key].join('_')
        } else {
            autoInit(val, nameStack.concat(key))
        }
    }
}

Вопрос adiga связан еще с одним вариантом, который, вероятно, лучше, так как он более общий. Вы можете получить доступ к подтипам с помощью MyObjects['Troll'], но я уверен, что вы не сможете сделать это автоматически на любой глубине, как вы можете с помощью двух вышеупомянутых опций.

0
ответ дан Gerrit0 18 January 2019 в 21:12
поделиться
Другие вопросы по тегам:

Похожие вопросы: